diff -Nur kvm-76/Makefile kvm-userspace/Makefile --- kvm-76/Makefile 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/Makefile 2008-10-12 09:38:23.000000000 +0200 @@ -23,7 +23,7 @@ ifneq '$(filter $(ARCH), i386 x86_64)' '' qemu: extboot endif -ifneq '$(filter $(ARCH), powerpc, ia64)' '' +ifneq '$(filter $(ARCH), powerpc ia64)' '' qemu: libfdt endif user: libkvm diff -Nur kvm-76/SOURCES kvm-userspace/SOURCES --- kvm-76/SOURCES 2008-09-28 20:38:01.000000000 +0200 +++ kvm-userspace/SOURCES 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -kernel: ac8e90a2b855305d7459fd6e40966e09bff38cb5 -userspace: 0795a0b426419c3539355b0aeeb17f6c5e0474a8 diff -Nur kvm-76/bios/acpi-dsdt.dsl kvm-userspace/bios/acpi-dsdt.dsl --- kvm-76/bios/acpi-dsdt.dsl 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/bios/acpi-dsdt.dsl 2008-10-12 09:38:23.000000000 +0200 @@ -224,7 +224,7 @@ 0x00000000, // Address Translation Offset 0x00020000, // Address Length ,, , AddressRangeMemory, TypeStatic) - DWordMemory (ResourceProducer, PosDecode, MinNotFixed, MaxFixed, NonCacheable, ReadWrite, + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, // Address Space Granularity 0xE0000000, // Address Range Minimum 0xFEBFFFFF, // Address Range Maximum diff -Nur kvm-76/bios/rombios.c kvm-userspace/bios/rombios.c --- kvm-76/bios/rombios.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/bios/rombios.c 2008-10-12 09:38:23.000000000 +0200 @@ -175,6 +175,7 @@ #define IPL_TABLE_ENTRIES 8 #define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */ #define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */ +#define IPL_BOOTFIRST_OFFSET 0x0084 /* u16: user selected device */ #define IPL_SIZE 0xff #define IPL_TYPE_FLOPPY 0x01 #define IPL_TYPE_HARDDISK 0x02 @@ -665,7 +666,8 @@ chs_t lchs; // Logical CHS chs_t pchs; // Physical CHS - Bit32u sectors; // Total sectors count + Bit32u sectors_low; // Total sectors count + Bit32u sectors_high; } ata_device_t; typedef struct { @@ -712,8 +714,8 @@ // The EBDA structure should conform to // http://www.frontiernet.net/~fys/rombios.htm document // I made the ata and cdemu structs begin at 0x121 in the EBDA seg - // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot - // device tables are at 0x9ff00 -- 0x9ffff + // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot + // device tables are at IPL_SEG typedef struct { unsigned char filler1[0x3D]; @@ -901,7 +903,10 @@ static void keyboard_panic(); static void shutdown_status_panic(); static void nmi_handler_msg(); +static void delay_ticks(); +static void delay_ticks_and_check_for_keystroke(); +static void interactive_bootkey(); static void print_bios_banner(); static void print_boot_device(); static void print_boot_failure(); @@ -1515,17 +1520,94 @@ send(action, val - (nval * 10) + '0'); } -void put_str(action, s) +void put_str(action, segment, offset) Bit16u action; - Bit8u *s; + Bit16u segment; + Bit16u offset; { Bit8u c; - if (!s) - s = ""; - while (c = read_byte(get_CS(), s)) { + while (c = read_byte(segment, offset)) { send(action, c); - s++; + offset++; + } +} + + void +delay_ticks(ticks) + Bit16u ticks; +{ + long ticks_to_wait, delta; + Bit32u prev_ticks, t; + + /* + * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock. + * We also have to be careful about interrupt storms. + */ +ASM_START + pushf + sti +ASM_END + ticks_to_wait = ticks; + prev_ticks = read_dword(0x0, 0x46c); + do + { +ASM_START + hlt +ASM_END + t = read_dword(0x0, 0x46c); + if (t > prev_ticks) + { + delta = t - prev_ticks; /* The temp var is required or bcc screws up. */ + ticks_to_wait -= delta; + } + else if (t < prev_ticks) + { + ticks_to_wait -= t; /* wrapped */ + } + + prev_ticks = t; + } while (ticks_to_wait > 0); +ASM_START + cli + popf +ASM_END +} + + Bit8u +check_for_keystroke() +{ +ASM_START + mov ax, #0x100 + int #0x16 + jz no_key + mov al, #1 + jmp done +no_key: + xor al, al +done: +ASM_END +} + + Bit8u +get_keystroke() +{ +ASM_START + mov ax, #0x0 + int #0x16 + xchg ah, al +ASM_END +} + + void +delay_ticks_and_check_for_keystroke(ticks, count) + Bit16u ticks, count; +{ + Bit16u i; + for (i = 1; i <= count; i++) { + delay_ticks(ticks); + if (check_for_keystroke()) + break; } } @@ -1534,7 +1616,7 @@ // A compact variable argument printf function. // // Supports %[format_width][length]format -// where format can be x,X,u,d,s,c +// where format can be x,X,u,d,s,S,c // and the optional length modifier is l (ell) //-------------------------------------------------------------------------- void @@ -1623,7 +1705,13 @@ put_int(action, arg, format_width, 0); } else if (c == 's') { - put_str(action, arg); + put_str(action, get_CS(), arg); + } + else if (c == 'S') { + hibyte = arg; + arg_ptr++; + arg = read_word(arg_seg, arg_ptr); + put_str(action, hibyte, arg); } else if (c == 'c') { send(action, arg); @@ -1869,6 +1957,7 @@ // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm //-------------------------------------------------------------------------- +static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"}; static void init_boot_vectors() @@ -1880,6 +1969,9 @@ /* Clear out the IPL table. */ memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE); + /* User selected device not set */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF); + /* Floppy drive */ e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); @@ -1917,22 +2009,106 @@ return 1; } +#if BX_ELTORITO_BOOT + void +interactive_bootkey() +{ + ipl_entry_t e; + Bit16u count; + char description[33]; + Bit8u scan_code; + Bit8u i; + Bit16u ss = get_SS(); + Bit16u valid_choice = 0; + + while (check_for_keystroke()) + get_keystroke(); + + printf("Press F12 for boot menu.\n\n"); + + delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */ + if (check_for_keystroke()) + { + scan_code = get_keystroke(); + if (scan_code == 0x58) /* F12 */ + { + while (check_for_keystroke()) + get_keystroke(); + + printf("Select boot device:\n\n"); + + count = read_word(IPL_SEG, IPL_COUNT_OFFSET); + for (i = 0; i < count; i++) + { + memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e)); + printf("%d. ", i+1); + switch(e.type) + { + case IPL_TYPE_FLOPPY: + case IPL_TYPE_HARDDISK: + case IPL_TYPE_CDROM: + printf("%s\n", drivetypes[e.type]); + break; + case IPL_TYPE_BEV: + printf("%s", drivetypes[4]); + if (e.description != 0) + { + memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32); + description[32] = 0; + printf(" [%S]", ss, description); + } + printf("\n"); + break; + } + } + + count++; + while (!valid_choice) { + scan_code = get_keystroke(); + if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */ + { + valid_choice = 1; + } + else if (scan_code <= count) + { + valid_choice = 1; + scan_code -= 1; + /* Set user selected device */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code); + } + } + printf("\n"); + } + } +} +#endif // BX_ELTORITO_BOOT //-------------------------------------------------------------------------- // print_boot_device // displays the boot device //-------------------------------------------------------------------------- -static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"}; - void -print_boot_device(type) - Bit16u type; +print_boot_device(e) + ipl_entry_t *e; { + Bit16u type; + char description[33]; + Bit16u ss = get_SS(); + type = e->type; /* NIC appears as type 0x80 */ if (type == IPL_TYPE_BEV) type = 0x4; if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n"); - printf("Booting from %s...\n", drivetypes[type]); + printf("Booting from %s", drivetypes[type]); + /* print product string if BEV */ + if (type == 4 && e->description != 0) { + /* first 32 bytes are significant */ + memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32); + /* terminate string */ + description[32] = 0; + printf(" [%S]", ss, description); + } + printf("...\n"); } //-------------------------------------------------------------------------- @@ -1945,15 +2121,15 @@ { if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n"); - printf("Boot from %s failed", drivetypes[type]); + printf("Boot failed"); if (type < 4) { /* Report the reason too */ - if (reason==0) - printf(": not a bootable disk"); - else - printf(": could not read the boot disk"); + if (reason==0) + printf(": not a bootable disk"); + else + printf(": could not read the boot disk"); } - printf("\n"); + printf("\n\n"); } //-------------------------------------------------------------------------- @@ -2197,7 +2373,8 @@ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0); - write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L); } // hdidmap and cdidmap init. @@ -2359,7 +2536,7 @@ // Now we send a IDENTIFY command to ATA device if(type == ATA_TYPE_ATA) { - Bit32u sectors; + Bit32u sectors_low, sectors_high; Bit16u cylinders, heads, spt, blksize; Bit8u translation, removable, mode; @@ -2367,7 +2544,7 @@ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); - if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 ) + if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 ) BX_PANIC("ata-detect: Failed to detect ATA device\n"); removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0; @@ -2378,7 +2555,13 @@ heads = read_word(get_SS(),buffer+(3*2)); // word 3 spt = read_word(get_SS(),buffer+(6*2)); // word 6 - sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61 + if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support + sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101 + sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103 + } else { + sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61 + sectors_high = 0; + } write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable); @@ -2387,7 +2570,8 @@ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt); - write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high); BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt); translation = inb_cmos(0x39 + channel/2); @@ -2415,14 +2599,14 @@ break; case ATA_TRANSLATION_LBA: spt = 63; - sectors /= 63; - heads = sectors / 1024; + sectors_low /= 63; + heads = sectors_low / 1024; if (heads>128) heads = 255; else if (heads>64) heads = 128; else if (heads>32) heads = 64; else if (heads>16) heads = 32; else heads=16; - cylinders = sectors / heads; + cylinders = sectors_low / heads; break; case ATA_TRANSLATION_RECHS: // Take care not to overflow @@ -2465,7 +2649,7 @@ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); - if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0) + if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0) BX_PANIC("ata-detect: Failed to detect ATAPI device\n"); type = read_byte(get_SS(),buffer+1) & 0x1f; @@ -2490,8 +2674,8 @@ switch (type) { case ATA_TYPE_ATA: - sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors); - sizeinmb >>= 11; + sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21) + | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11); case ATA_TYPE_ATAPI: // Read ATA/ATAPI version ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160); @@ -2504,7 +2688,7 @@ for(i=0;i<20;i++){ write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1)); write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54)); - } + } // Reformat write_byte(get_SS(),model+40,0x00); @@ -2512,7 +2696,13 @@ if(read_byte(get_SS(),model+i)==0x20) write_byte(get_SS(),model+i,0x00); else break; + } + if (i>36) { + write_byte(get_SS(),model+36,0x00); + for(i=35;i>32;i--){ + write_byte(get_SS(),model+i,0x2E); } + } break; } @@ -2628,9 +2818,9 @@ // 5 : more sectors to read/verify // 6 : no sectors left to write // 7 : more sectors to write -Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset) +Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) Bit16u device, command, count, cylinder, head, sector, segment, offset; -Bit32u lba; +Bit32u lba_low, lba_high; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2, blksize; @@ -2659,19 +2849,19 @@ // sector will be 0 only on lba access. Convert to lba-chs if (sector == 0) { - if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) { + if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); - outb(iobase1 + ATA_CB_SN, lba >> 24); - outb(iobase1 + ATA_CB_CL, 0); - outb(iobase1 + ATA_CB_CH, 0); + outb(iobase1 + ATA_CB_SN, lba_low >> 24); + outb(iobase1 + ATA_CB_CL, lba_high & 0xff); + outb(iobase1 + ATA_CB_CH, lba_high >> 8); command |= 0x04; count &= (1UL << 8) - 1; - lba &= (1UL << 24) - 1; + lba_low &= (1UL << 24) - 1; } - sector = (Bit16u) (lba & 0x000000ffL); - cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL); - head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; + sector = (Bit16u) (lba_low & 0x000000ffL); + cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); + head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; } outb(iobase1 + ATA_CB_FR, 0x00); @@ -2779,9 +2969,9 @@ // 5 : more sectors to read/verify // 6 : no sectors left to write // 7 : more sectors to write -Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset) +Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) Bit16u device, command, count, cylinder, head, sector, segment, offset; -Bit32u lba; +Bit32u lba_low, lba_high; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2, blksize; @@ -2810,19 +3000,19 @@ // sector will be 0 only on lba access. Convert to lba-chs if (sector == 0) { - if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) { + if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); - outb(iobase1 + ATA_CB_SN, lba >> 24); - outb(iobase1 + ATA_CB_CL, 0); - outb(iobase1 + ATA_CB_CH, 0); + outb(iobase1 + ATA_CB_SN, lba_low >> 24); + outb(iobase1 + ATA_CB_CL, lba_high & 0xff); + outb(iobase1 + ATA_CB_CH, lba_high >> 8); command |= 0x04; count &= (1UL << 8) - 1; - lba &= (1UL << 24) - 1; + lba_low &= (1UL << 24) - 1; } - sector = (Bit16u) (lba & 0x000000ffL); - cylinder = (Bit16u) ((lba>>8) & 0x0000ffffL); - head = ((Bit16u) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; + sector = (Bit16u) (lba_low & 0x000000ffL); + cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); + head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; } outb(iobase1 + ATA_CB_FR, 0x00); @@ -3299,9 +3489,9 @@ BX_DEBUG_ATA("sectors=%u\n", sectors); if (block_len == 2048) sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ - if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors)) + if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low)) printf("%dMB medium detected\n", sectors>>(20-9)); - write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors); + write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors); return 0; } @@ -4214,7 +4404,7 @@ break; } } -#endif +#endif // BX_USE_PS2_MOUSE void set_e820_range(ES, DI, start, end, extra_start, extra_end, type) @@ -4321,6 +4511,7 @@ extended_memory_size <<= 8; extended_memory_size |= inb_cmos(0x30); extended_memory_size *= 1024; + extended_memory_size += (1L * 1024 * 1024); } extra_lowbits_memory_size = inb_cmos(0x5c); @@ -4360,10 +4551,17 @@ return; break; case 3: +#if BX_ROMBIOS32 set_e820_range(ES, regs.u.r16.di, 0x00100000L, extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1); regs.u.r32.ebx = 4; +#else + set_e820_range(ES, regs.u.r16.di, + 0x00100000L, + extended_memory_size, 1); + regs.u.r32.ebx = 5; +#endif regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); @@ -5011,7 +5209,7 @@ int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { - Bit32u lba; + Bit32u lba_low, lba_high; Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u cylinder, head, sector; Bit16u segment, offset; @@ -5090,14 +5288,15 @@ // if needed, translate lchs to lba, and execute command if ( (nph != nlh) || (npspt != nlspt)) { - lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1; + lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1; + lba_high = 0; sector = 0; // this forces the command to be lba } if ( GET_AH() == 0x02 ) - status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset); + status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); else - status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset); + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); // Set nb of sector transferred SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors)); @@ -5159,9 +5358,9 @@ nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); // Compute sector count seen by int13 - lba = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt; - CX = lba >> 16; - DX = lba & 0xffff; + lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt; + CX = lba_low >> 16; + DX = lba_low & 0xffff; SET_AH(3); // hard disk accessible goto int13_success_noah; @@ -5183,16 +5382,17 @@ segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment); offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset); - // Can't use 64 bits lba - lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); - if (lba != 0L) { - BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH()); + // Get 32 msb lba and check + lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); + if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) { + BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); goto int13_fail; } - // Get 32 bits lba and check - lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); - if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) { + // Get 32 lsb lba and check + lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); + if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) + && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) { BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); goto int13_fail; } @@ -5203,9 +5403,9 @@ // Execute the command if ( GET_AH() == 0x42 ) - status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset); + status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); else - status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset); + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); count=read_word(ebda_seg, &EbdaData->ata.trsfsectors); write_word(DS, SI+(Bit16u)&Int13Ext->count, count); @@ -5243,11 +5443,12 @@ npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders); nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads); npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt); - lba = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors); + lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low); + lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high); blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a); - if ((lba/npspt)/nph > 0x3fff) + if (lba_high || (lba_low/npspt)/nph > 0x3fff) { write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff); @@ -5259,8 +5460,8 @@ } write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph); write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt); - write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba); // FIXME should be Bit64 - write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L); + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low); + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high); write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize); } @@ -7779,6 +7980,7 @@ Bit16u bootseg; Bit16u bootip; Bit16u status; + Bit16u bootfirst; ipl_entry_t e; @@ -7806,7 +8008,16 @@ bootdev |= ((inb_cmos(0x38) & 0xf0) << 4); bootdev >>= 4 * seq_nr; bootdev &= 0xf; - if (bootdev == 0) BX_PANIC("No bootable device.\n"); + + /* Read user selected device */ + bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET); + if (bootfirst != 0xFFFF) { + bootdev = bootfirst; + /* User selected device not set */ + write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF); + /* Reset boot sequence */ + write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF); + } else if (bootdev == 0) BX_PANIC("No bootable device.\n"); /* Translate from CMOS runes to an IPL table offset by subtracting 1 */ bootdev -= 1; @@ -7827,7 +8038,7 @@ /* Do the loading, and set up vector as a far pointer to the boot * address, and bootdrv as the boot drive */ - print_boot_device(e.type); + print_boot_device(&e); switch(e.type) { case IPL_TYPE_FLOPPY: /* FDD */ @@ -7899,9 +8110,7 @@ bootdrv = (Bit8u)(status>>8); bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment); - /* Canonicalize bootseg:bootip */ - bootip = (bootseg & 0x0fff) << 4; - bootseg &= 0xf000; + bootip = 0; break; #endif @@ -7919,6 +8128,8 @@ /* Jump to the boot vector */ ASM_START mov bp, sp + push cs + push #int18_handler ;; Build an iret stack frame that will take us to the boot vector. ;; iret pops ip, then cs, then flags, so push them in the opposite order. pushf @@ -8874,13 +9085,33 @@ ;-------------------- ; relocated here because the primary POST area isnt big enough. eoi_jmp_post: - call eoi_both_pics + mov al, #0x20 + out #0xA0, al ;; slave PIC EOI + mov al, #0x20 + out #0x20, al ;; master PIC EOI +jmp_post_0x467: xor ax, ax mov ds, ax jmp far ptr [0x467] +iret_post_0x467: + xor ax, ax + mov ds, ax + + mov sp, [0x467] + mov ss, [0x469] + iret + +retf_post_0x467: + xor ax, ax + mov ds, ax + + mov sp, [0x467] + mov ss, [0x469] + retf + ;-------------------- eoi_both_pics: @@ -9447,10 +9678,10 @@ db 0x08 ;; PCI interrupt router DevFunc dw 0x0000 ;; PCI exclusive IRQs dw 0x8086 ;; compatible PCI interrupt router vendor ID - dw 0x7000 ;; compatible PCI interrupt router device ID + dw 0x122e ;; compatible PCI interrupt router device ID dw 0,0 ;; Miniport data db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved - db 0x07 ;; checksum + db 0x37 ;; checksum pci_routing_table_structure_start: ;; first slot entry PCI-to-ISA (embedded) db 0 ;; pci bus number @@ -9752,7 +9983,7 @@ pop bp pop ds ret -#endif // BX_ROMBIOS32 +#endif // !BX_ROMBIOS32 #endif // BX_PCIBIOS #if BX_ROMBIOS32 @@ -9872,7 +10103,7 @@ dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18) dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff -#endif +#endif // BX_ROMBIOS32 ; parallel port detection: base address in DX, index in BX, timeout in CL @@ -10056,10 +10287,12 @@ mov ds, ax ret -;; for 'C' strings and other data, insert them here with -;; a the following hack: -;; DATA_SEG_DEFS_HERE - +post_enable_cache: + ;; enable cache + mov eax, cr0 + and eax, #0x9fffffff + mov cr0, eax + jmp post_enable_cache_done ;; the following area can be used to write dynamically generated tables .align 16 @@ -10067,16 +10300,15 @@ dd 0xaafb4442 dd bios_table_area_end - bios_table_area_start - 8; + ;-------- ;- POST - ;-------- .org 0xe05b ; POST Entry Point post: - ;; enable cache - mov eax, cr0 - and eax, #0x9fffffff - mov cr0, eax - + jmp post_enable_cache ; hack: we have limited space before next .org, + ; so take this bit out-of-line +post_enable_cache_done: xor ax, ax ;; first reset the DMA controllers @@ -10118,8 +10350,20 @@ cmp al, #0x05 je eoi_jmp_post + ;; 0x0A = jmp via [0x40:0x67] jump + cmp al, #0x0a + je jmp_post_0x467 + + ;; 0x0B = iret via [0x40:0x67] + cmp al, #0x0b + je iret_post_0x467 + + ;; 0x0C = retf via [0x40:0x67] + cmp al, #0x0c + je retf_post_0x467 + ;; Examine CMOS shutdown status. - ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status. + ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status. push bx call _shutdown_status_panic @@ -10356,25 +10600,19 @@ ;; call floppy_drive_post -#if BX_USE_ATADRV - ;; ;; Hard Drive setup ;; call hard_drive_post +#if BX_USE_ATADRV + ;; ;; ATA/ATAPI driver setup ;; call _ata_init call _ata_detect ;; -#else // BX_USE_ATADRV - - ;; - ;; Hard Drive setup - ;; - call hard_drive_post #endif // BX_USE_ATADRV @@ -10392,6 +10630,10 @@ mov ax, #0xe000 call rom_scan +#if BX_ELTORITO_BOOT + call _interactive_bootkey +#endif // BX_ELTORITO_BOOT + sti ;; enable interrupts int #0x19 diff -Nur kvm-76/bios/rombios32.c kvm-userspace/bios/rombios32.c --- kvm-76/bios/rombios32.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/bios/rombios32.c 2008-10-12 09:38:23.000000000 +0200 @@ -416,7 +416,7 @@ uint32_t cpuid_features; uint32_t cpuid_ext_features; unsigned long ram_size; -uint64_t above4g_ram_size; +uint64_t ram_end; uint8_t bios_uuid[16]; #ifdef BX_USE_EBDA_TABLES unsigned long ebda_cur_addr; @@ -452,7 +452,7 @@ // check if backdoor port exists asm volatile ("outl %%eax, %%dx" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) - : "a" (0x564d5868), "c" (0xa), "d" (0x5658)); + : "a" (0x564d5868), "b" (0), "c" (0xa), "d" (0x5658)); if (ebx == 0x564d5868) { uint32_t *uuid_ptr = (uint32_t *)bios_uuid; // get uuid @@ -494,7 +494,6 @@ uint8_t valb[8]; uint64_t val; } u; - uint64_t vbase, vmask; mtrr_cap = rdmsr(MSR_MTRRcap); vcnt = mtrr_cap & 0xff; @@ -521,25 +520,10 @@ wrmsr_smp(MSR_MTRRfix4K_E8000, 0); wrmsr_smp(MSR_MTRRfix4K_F0000, 0); wrmsr_smp(MSR_MTRRfix4K_F8000, 0); - vbase = 0; - --vcnt; /* leave one mtrr for VRAM */ - for (i = 0; i < vcnt && vbase < ram_size; ++i) { - vmask = (1ull << 40) - 1; - while (vbase + vmask + 1 > ram_size) - vmask >>= 1; - wrmsr_smp(MTRRphysBase_MSR(i), vbase | 6); - wrmsr_smp(MTRRphysMask_MSR(i), (~vmask & 0xfffffff000ull) | 0x800); - vbase += vmask + 1; - } - for (vbase = 1ull << 32; i < vcnt && vbase < above4g_ram_size; ++i) { - vmask = (1ull << 40) - 1; - while (vbase + vmask + 1 > above4g_ram_size) - vmask >>= 1; - wrmsr_smp(MTRRphysBase_MSR(i), vbase | 6); - wrmsr_smp(MTRRphysMask_MSR(i), (~vmask & 0xfffffff000ull) | 0x800); - vbase += vmask + 1; - } - wrmsr_smp(MSR_MTRRdefType, 0xc00); + /* Mark 3.5-4GB as UC, anything not specified defaults to WB */ + wrmsr_smp(MTRRphysBase_MSR(0), 0xe0000000ull | 0); + wrmsr_smp(MTRRphysMask_MSR(0), ~(0x20000000ull - 1) | 0x800); + wrmsr_smp(MSR_MTRRdefType, 0xc06); } void ram_probe(void) @@ -551,17 +535,19 @@ ram_size = (cmos_readb(0x17) | (cmos_readb(0x18) << 8)) * 1024; if (cmos_readb(0x5b) | cmos_readb(0x5c) | cmos_readb(0x5d)) - above4g_ram_size = ((uint64_t)cmos_readb(0x5b) << 16) | - ((uint64_t)cmos_readb(0x5c) << 24) | ((uint64_t)cmos_readb(0x5d) << 32); + ram_end = (((uint64_t)cmos_readb(0x5b) << 16) | + ((uint64_t)cmos_readb(0x5c) << 24) | + ((uint64_t)cmos_readb(0x5d) << 32)) + (1ull << 32); + else + ram_end = ram_size; - if (above4g_ram_size) - above4g_ram_size += 1ull << 32; + BX_INFO("end of ram=%ldMB\n", ram_end >> 20); + BX_INFO("ram_size=0x%08lx\n", ram_size); #ifdef BX_USE_EBDA_TABLES - ebda_cur_addr = ((*(uint16_t *)(0x40e)) << 4) + 0x380; + ebda_cur_addr = ((*(uint16_t *)(0x40e)) << 4) + 0x380; + BX_INFO("ebda_cur_addr: 0x%08lx\n", ebda_cur_addr); #endif - BX_INFO("ram_size=0x%08lx\n", ram_size); - BX_INFO("top of ram %ldMB\n", above4g_ram_size >> 20); setup_mtrr(); } @@ -635,6 +621,17 @@ #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */ +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_APPLE 0x106b + typedef struct PCIDevice { int bus; int devfn; @@ -773,11 +770,13 @@ vendor_id = pci_config_readw(d, PCI_VENDOR_ID); device_id = pci_config_readw(d, PCI_DEVICE_ID); - if (vendor_id == 0x8086 && device_id == 0x7000) { + if (vendor_id == PCI_VENDOR_ID_INTEL && + (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 || + device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) { int i, irq; uint8_t elcr[2]; - /* PIIX3 bridge */ + /* PIIX3/PIIX4 PCI to ISA bridge */ elcr[0] = 0x00; elcr[1] = 0x00; @@ -790,9 +789,9 @@ } outb(0x4d0, elcr[0]); outb(0x4d1, elcr[1]); - BX_INFO("PIIX3 init: elcr=%02x %02x\n", + BX_INFO("PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); - } else if (vendor_id == 0x8086 && device_id == 0x1237) { + } else if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82441) { /* i440 PCI bridge */ bios_shadow_init(d); } @@ -853,8 +852,10 @@ d->bus, d->devfn, vendor_id, device_id); switch(class) { case 0x0101: - if (vendor_id == 0x8086 && device_id == 0x7010) { - /* PIIX3 IDE */ + if (vendor_id == PCI_VENDOR_ID_INTEL && + (device_id == PCI_DEVICE_ID_INTEL_82371SB_1 || + device_id == PCI_DEVICE_ID_INTEL_82371AB)) { + /* PIIX3/PIIX4 IDE */ pci_config_writew(d, 0x40, 0x8000); // enable IDE0 pci_config_writew(d, 0x42, 0x8000); // enable IDE1 goto default_map; @@ -874,7 +875,7 @@ break; case 0x0800: /* PIC */ - if (vendor_id == 0x1014) { + if (vendor_id == PCI_VENDOR_ID_IBM) { /* IBM */ if (device_id == 0x0046 || device_id == 0xFFFF) { /* MPIC & MPIC2 */ @@ -883,7 +884,7 @@ } break; case 0xff00: - if (vendor_id == 0x0106b && + if (vendor_id == PCI_VENDOR_ID_APPLE && (device_id == 0x0017 || device_id == 0x0022)) { /* macio bridge */ pci_set_io_region_addr(d, 0, 0x80800000); @@ -926,7 +927,7 @@ pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); } - if (vendor_id == 0x8086 && device_id == 0x7113) { + if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82371AB_3) { /* PIIX4 Power Management device (for ACPI) */ // acpi sci is hardwired to 9 @@ -1494,8 +1495,8 @@ fadt->pm1_evt_len = 4; fadt->pm1_cnt_len = 2; fadt->pm_tmr_len = 4; - fadt->plvl2_lat = cpu_to_le16(0x0fff); // C2 state not supported - fadt->plvl3_lat = cpu_to_le16(0x0fff); // C3 state not supported + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported fadt->gpe0_blk = cpu_to_le32(0xafe0); fadt->gpe0_blk_len = 4; /* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC */ @@ -1650,6 +1651,9 @@ uint16_t current_speed; uint8_t status; uint8_t processor_upgrade; + uint16_t l1_cache_handle; + uint16_t l2_cache_handle; + uint16_t l3_cache_handle; } __attribute__((__packed__)); /* SMBIOS type 16 - Physical Memory Array @@ -1772,8 +1776,8 @@ p->bios_release_date_str = 2; p->bios_rom_size = 0; /* FIXME */ - memset(p->bios_characteristics, 0, 7); - p->bios_characteristics[7] = 0x08; /* BIOS characteristics not supported */ + memset(p->bios_characteristics, 0, 8); + p->bios_characteristics[0] = 0x08; /* BIOS characteristics not supported */ p->bios_characteristics_extension_bytes[0] = 0; p->bios_characteristics_extension_bytes[1] = 0; @@ -1876,6 +1880,10 @@ p->status = 0x41; /* socket populated, CPU enabled */ p->processor_upgrade = 0x01; /* other */ + p->l1_cache_handle = 0xffff; /* cache information structure not provided */ + p->l2_cache_handle = 0xffff; + p->l3_cache_handle = 0xffff; + start += sizeof(struct smbios_type_4); memcpy((char *)start, "CPU " "\0" "" "\0" "", 7); @@ -1949,7 +1957,7 @@ p->header.handle = 0x1300; p->starting_address = 0; - p->ending_address = (memory_size_mb-1) * 1024; + p->ending_address = (memory_size_mb * 1024) - 1; p->memory_array_handle = 0x1000; p->partition_width = 1; @@ -1970,7 +1978,7 @@ p->header.handle = 0x1400; p->starting_address = 0; - p->ending_address = (memory_size_mb-1)*1024; + p->ending_address = (memory_size_mb * 1024) - 1; p->memory_device_handle = 0x1100; p->memory_array_mapped_address_handle = 0x1300; p->partition_row_position = 1; @@ -2021,7 +2029,8 @@ { unsigned cpu_num, nr_structs = 0, max_struct_size = 0; char *start, *p, *q; - int memsize = ram_size / (1024 * 1024); + int memsize = (ram_end == ram_size) ? ram_size / (1024 * 1024) : + (ram_end - (1ull << 32) + ram_size) / (1024 * 1024); #ifdef BX_USE_EBDA_TABLES ebda_cur_addr = align(ebda_cur_addr, 16); @@ -2048,8 +2057,8 @@ add_struct(smbios_type_4_init(p, cpu_num)); add_struct(smbios_type_16_init(p, memsize)); add_struct(smbios_type_17_init(p, memsize)); - add_struct(smbios_type_19_init(p, memsize)); - add_struct(smbios_type_20_init(p, memsize)); + add_struct(smbios_type_19_init(p, ram_end / (1024 * 1024))); + add_struct(smbios_type_20_init(p, ram_end / (1024 * 1024))); add_struct(smbios_type_32_init(p)); add_struct(smbios_type_127_init(p)); @@ -2082,14 +2091,14 @@ smp_probe(); - uuid_probe(); - pci_bios_init(); if (bios_table_cur_addr != 0) { mptable_init(); + uuid_probe(); + smbios_init(); if (acpi_enabled) diff -Nur kvm-76/bios/rombios32start.S kvm-userspace/bios/rombios32start.S --- kvm-76/bios/rombios32start.S 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/bios/rombios32start.S 2008-10-12 09:38:23.000000000 +0200 @@ -60,7 +60,7 @@ jmp 11b 12: - incw CPU_COUNT_ADDR + lock incw CPU_COUNT_ADDR ljmp $0xe000, $(1f-_start) 1: hlt diff -Nur kvm-76/configure kvm-userspace/configure --- kvm-76/configure 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/configure 2008-10-12 09:38:23.000000000 +0200 @@ -1,6 +1,7 @@ #!/bin/bash prefix=/usr/local +kernelsourcedir= kerneldir=/lib/modules/$(uname -r)/build cc=gcc ld=ld @@ -107,6 +108,11 @@ qemu_ldflags="$qemu_ldflags -L $PWD/libfdt" fi +# see if we have split build and source directories +if [ -d "$kerneldir/include2" ]; then + kernelsourcedir=${kerneldir%/*}/source +fi + #configure user dir (cd user; ./configure --prefix="$prefix" --kerneldir="$libkvm_kerneldir" \ --arch="$arch" \ @@ -129,6 +135,7 @@ ARCH=$arch PREFIX=$prefix KERNELDIR=$kerneldir +KERNELSOURCEDIR=$kernelsourcedir LIBKVM_KERNELDIR=$libkvm_kerneldir WANT_MODULE=$want_module CROSS_COMPILE=$cross_prefix diff -Nur kvm-76/kernel/Makefile kvm-userspace/kernel/Makefile --- kvm-76/kernel/Makefile 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/kernel/Makefile 2008-10-12 09:38:23.000000000 +0200 @@ -41,7 +41,9 @@ all:: header-link prerequisite # include header priority 1) $LINUX 2) $KERNELDIR 3) include-compat $(MAKE) -C $(KERNELDIR) M=`pwd` \ - LINUXINCLUDE="-I`pwd`/include -Iinclude -Iarch/${ARCH_DIR}/include -I`pwd`/include-compat \ + LINUXINCLUDE="-I`pwd`/include -Iinclude \ + $(if $(KERNELSOURCEDIR),-Iinclude2 -I$(KERNELSOURCEDIR)/include) \ + -Iarch/${ARCH_DIR}/include -I`pwd`/include-compat \ -include include/linux/autoconf.h \ -include `pwd`/$(ARCH_DIR)/external-module-compat.h $(module_defines)" "$$@" diff -Nur kvm-76/kernel/ia64/Kbuild kvm-userspace/kernel/ia64/Kbuild --- kvm-76/kernel/ia64/Kbuild 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/kernel/ia64/Kbuild 2008-10-12 09:38:23.000000000 +0200 @@ -1,7 +1,7 @@ obj-m := kvm.o kvm-intel.o kvm-objs := kvm_main.o ioapic.o coalesced_mmio.o kvm-ia64.o kvm_fw.o \ - ../anon_inodes.o ../external-module-compat.o + irq_comm.o ../anon_inodes.o ../external-module-compat.o EXTRA_CFLAGS_vcpu.o += -mfixed-range=f2-f5,f12-f127 kvm-intel-objs := vmm.o vmm_ivt.o trampoline.o vcpu.o optvfault.o mmio.o \ diff -Nur kvm-76/libkvm/libkvm-powerpc.c kvm-userspace/libkvm/libkvm-powerpc.c --- kvm-76/libkvm/libkvm-powerpc.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/libkvm/libkvm-powerpc.c 2008-10-12 09:38:23.000000000 +0200 @@ -1,10 +1,7 @@ /* - * This header is for functions & variables that will ONLY be - * used inside libkvm for x86. - * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE - * WITHIN LIBKVM. - * - * derived from libkvm.c + * This file contains the powerpc specific implementation for the + * architecture dependent functions defined in kvm-common.h and + * libkvm.h * * Copyright (C) 2006 Qumranet, Inc. * @@ -12,12 +9,11 @@ * Avi Kivity * Yaniv Kamay * - * Copyright 2007 IBM Corporation. - * Added by & Authors: + * Copyright IBM Corp. 2007,2008 + * Authors: * Jerone Young * Christian Ehrhardt * - * * This work is licensed under the GNU LGPL license, version 2. */ diff -Nur kvm-76/libkvm/libkvm.c kvm-userspace/libkvm/libkvm.c --- kvm-76/libkvm/libkvm.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/libkvm/libkvm.c 2008-10-12 09:38:23.000000000 +0200 @@ -34,6 +34,7 @@ #include #include #include +#include #include "libkvm.h" #if defined(__x86_64__) || defined(__i386__) @@ -69,6 +70,7 @@ unsigned long len; unsigned long userspace_addr; unsigned flags; + int logging_count; }; struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS]; @@ -120,6 +122,7 @@ void free_slot(int slot) { slots[slot].len = 0; + slots[slot].logging_count = 0; } int get_slot(unsigned long phys_addr) @@ -158,17 +161,23 @@ /* * dirty pages logging control */ -static int kvm_dirty_pages_log_change(kvm_context_t kvm, unsigned long phys_addr - , __u32 flag) +static int kvm_dirty_pages_log_change(kvm_context_t kvm, + unsigned long phys_addr, + unsigned flags, + unsigned mask) { int r = -1; int slot = get_slot(phys_addr); + if (slot == -1) { fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__); return 1; } - flag |= slots[slot].flags; + flags = (slots[slot].flags & ~mask) | flags; + if (flags == slots[slot].flags) + return 0; + slots[slot].flags = flags; { struct kvm_userspace_memory_region mem = { @@ -176,11 +185,15 @@ .memory_size = slots[slot].len, .guest_phys_addr = slots[slot].phys_addr, .userspace_addr = slots[slot].userspace_addr, - .flags = flag, + .flags = slots[slot].flags, }; - + DPRINTF("slot %d start %llx len %llx flags %x\n", + mem.slot, + mem.guest_phys_addr, + mem.memory_size, + mem.flags); r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem); if (r == -1) fprintf(stderr, "%s: %m\n", __FUNCTION__); @@ -188,18 +201,59 @@ return r; } -static int kvm_dirty_pages_log_change_all(kvm_context_t kvm, __u32 flag) +static int kvm_dirty_pages_log_change_all(kvm_context_t kvm, + int (*change)(kvm_context_t kvm, + uint64_t start, + uint64_t len)) { int i, r; for (i=r=0; idirty_pages_log_all) return 0; kvm->dirty_pages_log_all = 1; - return kvm_dirty_pages_log_change_all(kvm, KVM_MEM_LOG_DIRTY_PAGES); + return kvm_dirty_pages_log_change_all(kvm, + kvm_dirty_pages_log_enable_slot); } /** @@ -220,7 +275,8 @@ if (!kvm->dirty_pages_log_all) return 0; kvm->dirty_pages_log_all = 0; - return kvm_dirty_pages_log_change_all(kvm, 0); + return kvm_dirty_pages_log_change_all(kvm, + kvm_dirty_pages_log_disable_slot); } @@ -430,6 +486,11 @@ memory.userspace_addr = (unsigned long)ptr; memory.slot = get_free_slot(kvm); + DPRINTF("slot %d start %llx len %llx flags %x\n", + memory.slot, + memory.guest_phys_addr, + memory.memory_size, + memory.flags); r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory); if (r == -1) { fprintf(stderr, "%s: %s", __func__, strerror(errno)); @@ -499,6 +560,11 @@ } memory.slot = slot; + DPRINTF("slot %d start %llx len %llx flags %x\n", + memory.slot, + memory.guest_phys_addr, + memory.memory_size, + memory.flags); r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory); if (r == -1) { fprintf(stderr, "destroy_userspace_phys_mem: %s", @@ -766,6 +832,11 @@ return kvm->callbacks->try_push_interrupts(kvm->opaque); } +int try_push_nmi(kvm_context_t kvm) +{ + return kvm->callbacks->try_push_nmi(kvm->opaque); +} + void post_kvm_run(kvm_context_t kvm, int vcpu) { kvm->callbacks->post_kvm_run(kvm->opaque, vcpu); @@ -790,6 +861,17 @@ return run->ready_for_interrupt_injection; } +int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu) +{ +#ifdef KVM_CAP_NMI + struct kvm_run *run = kvm->run[vcpu]; + + return run->ready_for_nmi_injection; +#else + return 0; +#endif +} + int kvm_run(kvm_context_t kvm, int vcpu) { int r; @@ -797,6 +879,9 @@ struct kvm_run *run = kvm->run[vcpu]; again: +#ifdef KVM_CAP_NMI + run->request_nmi_window = try_push_nmi(kvm); +#endif #if !defined(__s390__) if (!kvm->irqchip_in_kernel) run->request_interrupt_window = try_push_interrupts(kvm); @@ -872,6 +957,9 @@ r = handle_halt(kvm, vcpu); break; case KVM_EXIT_IRQ_WINDOW_OPEN: +#ifdef KVM_CAP_NMI + case KVM_EXIT_NMI_WINDOW_OPEN: +#endif break; case KVM_EXIT_SHUTDOWN: r = handle_shutdown(kvm, vcpu); @@ -956,6 +1044,15 @@ return r; } +int kvm_inject_nmi(kvm_context_t kvm, int vcpu) +{ +#ifdef KVM_CAP_NMI + return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI); +#else + return -ENOSYS; +#endif +} + int kvm_init_coalesced_mmio(kvm_context_t kvm) { int r = 0; diff -Nur kvm-76/libkvm/libkvm.h kvm-userspace/libkvm/libkvm.h --- kvm-76/libkvm/libkvm.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/libkvm/libkvm.h 2008-10-12 09:38:23.000000000 +0200 @@ -66,6 +66,7 @@ int (*shutdown)(void *opaque, int vcpu); int (*io_window)(void *opaque); int (*try_push_interrupts)(void *opaque); + int (*try_push_nmi)(void *opaque); void (*post_kvm_run)(void *opaque, int vcpu); int (*pre_kvm_run)(void *opaque, int vcpu); int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write); @@ -216,6 +217,17 @@ int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu); /*! + * \brief Check if a vcpu is ready for NMI injection + * + * This checks if vcpu is not already running in NMI context. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return boolean indicating NMI injection readiness + */ +int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu); + +/*! * \brief Read VCPU registers * * This gets the GP registers from the VCPU and outputs them @@ -503,6 +515,12 @@ void* bitmap, void* opaque)); int kvm_set_irq_level(kvm_context_t kvm, int irq, int level); +int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len); +int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, + uint64_t phys_start, + uint64_t len); /*! * \brief Enable dirty-pages-logging for all memory regions * @@ -578,6 +596,17 @@ #endif +/*! + * \brief Simulate an NMI + * + * This allows you to simulate a non-maskable interrupt. + * + * \param kvm Pointer to the current kvm_context + * \param vcpu Which virtual CPU should get dumped + * \return 0 on success + */ +int kvm_inject_nmi(kvm_context_t kvm, int vcpu); + #endif /*! diff -Nur kvm-76/patches/origin kvm-userspace/patches/origin --- kvm-76/patches/origin 2008-09-28 20:38:11.000000000 +0200 +++ kvm-userspace/patches/origin 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -v2.6.27-rc7-106-g6ef190c diff -Nur kvm-76/qemu/Makefile kvm-userspace/qemu/Makefile --- kvm-76/qemu/Makefile 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/Makefile 2008-10-12 09:38:23.000000000 +0200 @@ -81,6 +81,7 @@ OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o OBJS+=usb-serial.o usb-net.o OBJS+=sd.o ssi-sd.o +OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o ifdef CONFIG_BRLAPI OBJS+= baum.o @@ -165,6 +166,9 @@ curses.o: curses.c keymaps.c curses_keys.h $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< +bt-host.o: bt-host.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(CONFIG_BLUEZ_CFLAGS) -c -o $@ $< + audio/sdlaudio.o: audio/sdlaudio.c $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $< @@ -260,7 +264,7 @@ cscope: rm -f ./cscope.* - find . -name "*.[ch]" -print > ./cscope.files + find . -name "*.[ch]" -print | sed 's,^\./,,' > ./cscope.files cscope -b # documentation diff -Nur kvm-76/qemu/Makefile.target kvm-userspace/qemu/Makefile.target --- kvm-76/qemu/Makefile.target 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/Makefile.target 2008-10-12 09:38:23.000000000 +0200 @@ -214,7 +214,7 @@ endif ifeq ($(USE_KVM), 1) -LIBOBJS+=qemu-kvm.o kvm-compatfd.o +LIBOBJS+=qemu-kvm.o endif ifdef CONFIG_SOFTFLOAT LIBOBJS+=fpu/softfloat.o @@ -586,9 +586,13 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif +ifdef CONFIG_BLUEZ +LIBS += $(CONFIG_BLUEZ_LIBS) +endif + ifdef CONFIG_LIBFDT LIBS += -lfdt -DEPLIBS += libfdt.a +DEPLIBS += ../libfdt/libfdt.a endif # SCSI layer @@ -696,7 +700,7 @@ OBJS+= omap2.o omap_dss.o soc_dma.o OBJS+= palm.o tsc210x.o OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o -OBJS+= tsc2005.o +OBJS+= tsc2005.o bt-hci-csr.o OBJS+= mst_fpga.o mainstone.o OBJS+= musicpal.o pflash_cfi02.o CPPFLAGS += -DHAS_AUDIO diff -Nur kvm-76/qemu/audio/alsaaudio.c kvm-userspace/qemu/audio/alsaaudio.c --- kvm-76/qemu/audio/alsaaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/alsaaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -917,7 +917,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops alsa_pcm_ops = { +static const struct audio_pcm_ops alsa_pcm_ops = { alsa_init_out, alsa_fini_out, alsa_run_out, diff -Nur kvm-76/qemu/audio/audio.c kvm-userspace/qemu/audio/audio.c --- kvm-76/qemu/audio/audio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/audio.c 2008-10-12 09:38:23.000000000 +0200 @@ -212,7 +212,7 @@ char *u = r + sizeof (qemu_prefix) - 1; pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix); - pstrcat (r, len, s); + pstrcat (r, len + sizeof (qemu_prefix), s); for (i = 0; i < len; ++i) { u[i] = toupper (u[i]); @@ -467,16 +467,13 @@ } pstrcpy (optname, optlen, qemu_prefix); - optlen -= preflen; /* copy while upper-casing, including trailing zero */ for (i = 0; i <= preflen; ++i) { optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]); } pstrcat (optname, optlen, "_"); - optlen--; pstrcat (optname, optlen, opt->name); - optlen -= len; def = 1; switch (opt->tag) { diff -Nur kvm-76/qemu/audio/audio.h kvm-userspace/qemu/audio/audio.h --- kvm-76/qemu/audio/audio.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/audio.h 2008-10-12 09:38:23.000000000 +0200 @@ -170,4 +170,7 @@ #define audio_MAX(a, b) ((a)<(b)?(b):(a)) #endif +int wav_start_capture (CaptureState *s, const char *path, int freq, + int bits, int nchannels); + #endif /* audio.h */ diff -Nur kvm-76/qemu/audio/audio_int.h kvm-userspace/qemu/audio/audio_int.h --- kvm-76/qemu/audio/audio_int.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/audio_int.h 2008-10-12 09:38:23.000000000 +0200 @@ -81,7 +81,7 @@ int samples; LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; - struct audio_pcm_ops *pcm_ops; + const struct audio_pcm_ops *pcm_ops; LIST_ENTRY (HWVoiceOut) entries; } HWVoiceOut; @@ -99,7 +99,7 @@ int samples; LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; - struct audio_pcm_ops *pcm_ops; + const struct audio_pcm_ops *pcm_ops; LIST_ENTRY (HWVoiceIn) entries; } HWVoiceIn; @@ -140,7 +140,7 @@ struct audio_option *options; void *(*init) (void); void (*fini) (void *); - struct audio_pcm_ops *pcm_ops; + const struct audio_pcm_ops *pcm_ops; int can_be_default; int max_voices_out; int max_voices_in; diff -Nur kvm-76/qemu/audio/coreaudio.c kvm-userspace/qemu/audio/coreaudio.c --- kvm-76/qemu/audio/coreaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/coreaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -520,7 +520,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops coreaudio_pcm_ops = { +static const struct audio_pcm_ops coreaudio_pcm_ops = { coreaudio_init_out, coreaudio_fini_out, coreaudio_run_out, diff -Nur kvm-76/qemu/audio/dsoundaudio.c kvm-userspace/qemu/audio/dsoundaudio.c --- kvm-76/qemu/audio/dsoundaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/dsoundaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -1057,7 +1057,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops dsound_pcm_ops = { +static const struct audio_pcm_ops dsound_pcm_ops = { dsound_init_out, dsound_fini_out, dsound_run_out, diff -Nur kvm-76/qemu/audio/esdaudio.c kvm-userspace/qemu/audio/esdaudio.c --- kvm-76/qemu/audio/esdaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/esdaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -566,7 +566,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -struct audio_pcm_ops qesd_pcm_ops = { +static const struct audio_pcm_ops qesd_pcm_ops = { qesd_init_out, qesd_fini_out, qesd_run_out, diff -Nur kvm-76/qemu/audio/fmodaudio.c kvm-userspace/qemu/audio/fmodaudio.c --- kvm-76/qemu/audio/fmodaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/fmodaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -657,7 +657,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops fmod_pcm_ops = { +static const struct audio_pcm_ops fmod_pcm_ops = { fmod_init_out, fmod_fini_out, fmod_run_out, diff -Nur kvm-76/qemu/audio/noaudio.c kvm-userspace/qemu/audio/noaudio.c --- kvm-76/qemu/audio/noaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/noaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -145,7 +145,7 @@ (void) opaque; } -static struct audio_pcm_ops no_pcm_ops = { +static const struct audio_pcm_ops no_pcm_ops = { no_init_out, no_fini_out, no_run_out, diff -Nur kvm-76/qemu/audio/ossaudio.c kvm-userspace/qemu/audio/ossaudio.c --- kvm-76/qemu/audio/ossaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/ossaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -751,7 +751,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops oss_pcm_ops = { +static const struct audio_pcm_ops oss_pcm_ops = { oss_init_out, oss_fini_out, oss_run_out, diff -Nur kvm-76/qemu/audio/paaudio.c kvm-userspace/qemu/audio/paaudio.c --- kvm-76/qemu/audio/paaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/paaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -487,7 +487,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -struct audio_pcm_ops qpa_pcm_ops = { +static const struct audio_pcm_ops qpa_pcm_ops = { qpa_init_out, qpa_fini_out, qpa_run_out, diff -Nur kvm-76/qemu/audio/sdlaudio.c kvm-userspace/qemu/audio/sdlaudio.c --- kvm-76/qemu/audio/sdlaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/sdlaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -423,7 +423,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -static struct audio_pcm_ops sdl_pcm_ops = { +static const struct audio_pcm_ops sdl_pcm_ops = { sdl_init_out, sdl_fini_out, sdl_run_out, diff -Nur kvm-76/qemu/audio/wavaudio.c kvm-userspace/qemu/audio/wavaudio.c --- kvm-76/qemu/audio/wavaudio.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/wavaudio.c 2008-10-12 09:38:23.000000000 +0200 @@ -159,7 +159,7 @@ le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); le_store (hdr + 32, 1 << (bits16 + stereo), 2); - wav->f = qemu_fopen_file (conf.wav_path, "wb"); + wav->f = qemu_fopen (conf.wav_path, "wb"); if (!wav->f) { dolog ("Failed to open wave file `%s'\nReason: %s\n", conf.wav_path, strerror (errno)); @@ -218,7 +218,7 @@ ldebug ("wav_fini"); } -struct audio_option wav_options[] = { +static struct audio_option wav_options[] = { {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq, "Frequency", NULL, 0}, @@ -233,7 +233,7 @@ {NULL, 0, NULL, NULL, NULL, 0} }; -struct audio_pcm_ops wav_pcm_ops = { +static const struct audio_pcm_ops wav_pcm_ops = { wav_init_out, wav_fini_out, wav_run_out, diff -Nur kvm-76/qemu/audio/wavcapture.c kvm-userspace/qemu/audio/wavcapture.c --- kvm-76/qemu/audio/wavcapture.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/audio/wavcapture.c 2008-10-12 09:38:23.000000000 +0200 @@ -134,7 +134,7 @@ le_store (hdr + 28, freq << shift, 4); le_store (hdr + 32, 1 << shift, 2); - wav->f = qemu_fopen_file (path, "wb"); + wav->f = qemu_fopen (path, "wb"); if (!wav->f) { term_printf ("Failed to open wave file `%s'\nReason: %s\n", path, strerror (errno)); diff -Nur kvm-76/qemu/block-raw-posix.c kvm-userspace/qemu/block-raw-posix.c --- kvm-76/qemu/block-raw-posix.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/block-raw-posix.c 2008-10-12 09:38:23.000000000 +0200 @@ -84,10 +84,16 @@ reopen it to see if the disk has been changed */ #define FD_OPEN_TIMEOUT 1000 +/* posix-aio doesn't allow multiple outstanding requests to a single file + * descriptor. we implement a pool of dup()'d file descriptors to work + * around this */ +#define RAW_FD_POOL_SIZE 64 + typedef struct BDRVRawState { int fd; int type; unsigned int lseek_err_cnt; + int fd_pool[RAW_FD_POOL_SIZE]; #if defined(__linux__) /* linux floppy specific */ int fd_open_flags; @@ -109,6 +115,7 @@ { BDRVRawState *s = bs->opaque; int fd, open_flags, ret; + int i; posix_aio_init(); @@ -138,6 +145,8 @@ return ret; } s->fd = fd; + for (i = 0; i < RAW_FD_POOL_SIZE; i++) + s->fd_pool[i] = -1; #if defined(O_DIRECT) s->aligned_buf = NULL; if (flags & BDRV_O_DIRECT) { @@ -436,6 +445,7 @@ typedef struct RawAIOCB { BlockDriverAIOCB common; + int fd; struct aiocb aiocb; struct RawAIOCB *next; int ret; @@ -447,6 +457,38 @@ RawAIOCB *first_aio; } PosixAioState; +static int raw_fd_pool_get(BDRVRawState *s) +{ + int i; + + for (i = 0; i < RAW_FD_POOL_SIZE; i++) { + /* already in use */ + if (s->fd_pool[i] != -1) + continue; + + /* try to dup file descriptor */ + s->fd_pool[i] = dup(s->fd); + if (s->fd_pool[i] != -1) + return s->fd_pool[i]; + } + + /* we couldn't dup the file descriptor so just use the main one */ + return s->fd; +} + +static void raw_fd_pool_put(RawAIOCB *acb) +{ + BDRVRawState *s = acb->common.bs->opaque; + int i; + + for (i = 0; i < RAW_FD_POOL_SIZE; i++) { + if (s->fd_pool[i] == acb->fd) { + close(s->fd_pool[i]); + s->fd_pool[i] = -1; + } + } +} + static void posix_aio_read(void *opaque) { PosixAioState *s = opaque; @@ -487,6 +529,7 @@ if (ret == ECANCELED) { /* remove the request */ *pacb = acb->next; + raw_fd_pool_put(acb); qemu_aio_release(acb); } else if (ret != EINPROGRESS) { /* end of aio */ @@ -503,6 +546,7 @@ *pacb = acb->next; /* call the callback */ acb->common.cb(acb->common.opaque, ret); + raw_fd_pool_put(acb); qemu_aio_release(acb); break; } else { @@ -540,20 +584,30 @@ s->first_aio = NULL; s->fd = qemu_signalfd(&mask); + if (s->fd == -1) { + fprintf(stderr, "failed to create signalfd\n"); + return -errno; + } fcntl(s->fd, F_SETFL, O_NONBLOCK); qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, s); -#if defined(__linux__) && defined(__GLIBC_PREREQ) && !__GLIBC_PREREQ(2, 4) +#if defined(__linux__) { - /* XXX: aio thread exit seems to hang on RedHat 9 and this init - seems to fix the problem. */ struct aioinit ai; + memset(&ai, 0, sizeof(ai)); +#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 4) + ai.aio_threads = 64; + ai.aio_num = 64; +#else + /* XXX: aio thread exit seems to hang on RedHat 9 and this init + seems to fix the problem. */ ai.aio_threads = 1; ai.aio_num = 1; ai.aio_idle_time = 365 * 100000; +#endif aio_init(&ai); } #endif @@ -575,7 +629,8 @@ acb = qemu_aio_get(bs, cb, opaque); if (!acb) return NULL; - acb->aiocb.aio_fildes = s->fd; + acb->fd = raw_fd_pool_get(s); + acb->aiocb.aio_fildes = acb->fd; acb->aiocb.aio_sigevent.sigev_signo = SIGUSR2; acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; acb->aiocb.aio_buf = buf; @@ -682,6 +737,7 @@ break; } else if (*pacb == acb) { *pacb = acb->next; + raw_fd_pool_put(acb); qemu_aio_release(acb); break; } @@ -692,9 +748,22 @@ #else /* CONFIG_AIO */ static int posix_aio_init(void) { + return 0; } #endif /* CONFIG_AIO */ +static void raw_close_fd_pool(BDRVRawState *s) +{ + int i; + + for (i = 0; i < RAW_FD_POOL_SIZE; i++) { + if (s->fd_pool[i] != -1) { + close(s->fd_pool[i]); + s->fd_pool[i] = -1; + } + } +} + static void raw_close(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; @@ -706,6 +775,7 @@ qemu_free(s->aligned_buf); #endif } + raw_close_fd_pool(s); } static int raw_truncate(BlockDriverState *bs, int64_t offset) @@ -896,7 +966,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) { BDRVRawState *s = bs->opaque; - int fd, open_flags, ret; + int fd, open_flags, ret, i; posix_aio_init(); @@ -961,6 +1031,8 @@ return ret; } s->fd = fd; + for (i = 0; i < RAW_FD_POOL_SIZE; i++) + s->fd_pool[i] = -1; #if defined(__linux__) /* close fd so that we can reopen it as needed */ if (s->type == FTYPE_FD) { @@ -973,7 +1045,6 @@ } #if defined(__linux__) - /* Note: we do not have a reliable method to detect if the floppy is present. The current method is to try to open the floppy at every I/O and to keep it opened during a few hundreds of ms. */ @@ -989,6 +1060,7 @@ (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { close(s->fd); s->fd = -1; + raw_close_fd_pool(s); #ifdef DEBUG_FLOPPY printf("Floppy closed\n"); #endif @@ -1089,6 +1161,7 @@ if (s->fd >= 0) { close(s->fd); s->fd = -1; + raw_close_fd_pool(s); } fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK); if (fd >= 0) { diff -Nur kvm-76/qemu/block-vmdk.c kvm-userspace/qemu/block-vmdk.c --- kvm-76/qemu/block-vmdk.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/block-vmdk.c 2008-10-12 09:38:23.000000000 +0200 @@ -709,13 +709,13 @@ "createType=\"monolithicSparse\"\n" "\n" "# Extent description\n" - "RW %lu SPARSE \"%s\"\n" + "RW %" PRId64 " SPARSE \"%s\"\n" "\n" "# The Disk Data Base \n" "#DDB\n" "\n" "ddb.virtualHWVersion = \"%d\"\n" - "ddb.geometry.cylinders = \"%lu\"\n" + "ddb.geometry.cylinders = \"%" PRId64 "\"\n" "ddb.geometry.heads = \"16\"\n" "ddb.geometry.sectors = \"63\"\n" "ddb.adapterType = \"ide\"\n"; @@ -791,8 +791,9 @@ if ((temp_str = strrchr(real_filename, ':')) != NULL) real_filename = temp_str + 1; snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), - (unsigned long)total_size, real_filename, - (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), total_size / (63 * 16)); + total_size, real_filename, + (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), + total_size / (int64_t)(63 * 16)); /* write the descriptor */ lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); diff -Nur kvm-76/qemu/bt-host.c kvm-userspace/qemu/bt-host.c --- kvm-76/qemu/bt-host.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/bt-host.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,207 @@ +/* + * Wrap a host Bluetooth HCI socket in a struct HCIInfo. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "net.h" + +#ifndef _WIN32 +# include +# include +# include +# ifdef CONFIG_BLUEZ +# include +# include +# include +# else +# include "hw/bt.h" +# define HCI_MAX_FRAME_SIZE 1028 +# endif + +struct bt_host_hci_s { + struct HCIInfo hci; + int fd; + + uint8_t hdr[HCI_MAX_FRAME_SIZE]; + int len; +}; + +static void bt_host_send(struct HCIInfo *hci, + int type, const uint8_t *data, int len) +{ + struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; + uint8_t pkt = type; + struct iovec iv[2]; + int ret; + + iv[0].iov_base = &pkt; + iv[0].iov_len = 1; + iv[1].iov_base = (void *) data; + iv[1].iov_len = len; + + while ((ret = writev(s->fd, iv, 2)) < 0) + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } +} + +static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_COMMAND_PKT, data, len); +} + +static void bt_host_acl(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_ACLDATA_PKT, data, len); +} + +static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) +{ + bt_host_send(hci, HCI_SCODATA_PKT, data, len); +} + +static int bt_host_read_poll(void *opaque) +{ + struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; + + return !!s->hci.evt_recv; +} + +static void bt_host_read(void *opaque) +{ + struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; + uint8_t *pkt; + int pktlen; + + /* Seems that we can't read only the header first and then the amount + * of data indicated in the header because Linux will discard everything + * that's not been read in one go. */ + s->len = read(s->fd, s->hdr, sizeof(s->hdr)); + + if (s->len < 0) { + fprintf(stderr, "qemu: error %i reading HCI frame\n", errno); + return; + } + + pkt = s->hdr; + while (s->len --) + switch (*pkt ++) { + case HCI_EVENT_PKT: + if (s->len < 2) + goto bad_pkt; + + pktlen = MIN(pkt[1] + 2, s->len); + s->hci.evt_recv(s->hci.opaque, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + + /* TODO: if this is an Inquiry Result event, it's also + * interpreted by Linux kernel before we received it, possibly + * we should clean the kernel Inquiry cache through + * ioctl(s->fd, HCI_INQUIRY, ...). */ + break; + + case HCI_ACLDATA_PKT: + if (s->len < 4) + goto bad_pkt; + + pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); + s->hci.acl_recv(s->hci.opaque, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_SCODATA_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->len -= pktlen; + pkt += pktlen; + + default: + bad_pkt: + fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); + } +} + +static int bt_host_bdaddr_set(struct HCIInfo *hci, const uint8_t *bd_addr) +{ + return -ENOTSUP; +} + +struct HCIInfo *bt_host_hci(const char *id) +{ + struct bt_host_hci_s *s; + int fd = -1; +# ifdef CONFIG_BLUEZ + int dev_id = hci_devid(id); + struct hci_filter flt; + + if (dev_id < 0) { + fprintf(stderr, "qemu: `%s' not available\n", id); + return 0; + } + + fd = hci_open_dev(dev_id); + + /* XXX: can we ensure nobody else has the device opened? */ +# endif + + if (fd < 0) { + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + id, strerror(errno), errno); + return 0; + } + +# ifdef CONFIG_BLUEZ + hci_filter_clear(&flt); + hci_filter_all_ptypes(&flt); + hci_filter_all_events(&flt); + + if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + fprintf(stderr, "qemu: Can't set HCI filter on socket (%i)\n", errno); + return 0; + } +# endif + + s = qemu_mallocz(sizeof(struct bt_host_hci_s)); + s->fd = fd; + s->hci.cmd_send = bt_host_cmd; + s->hci.sco_send = bt_host_sco; + s->hci.acl_send = bt_host_acl; + s->hci.bdaddr_set = bt_host_bdaddr_set; + + qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, 0, s); + + return &s->hci; +} +#else +struct HCIInfo *bt_host_hci(const char *id) +{ + fprintf(stderr, "qemu: bluetooth passthrough not supported (yet)\n", errno); + + return 0; +} +#endif diff -Nur kvm-76/qemu/bt-vhci.c kvm-userspace/qemu/bt-vhci.c --- kvm-76/qemu/bt-vhci.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/bt-vhci.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,170 @@ +/* + * Support for host VHCIs inside qemu scatternets. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "net.h" +#include "hw/bt.h" + +#define VHCI_DEV "/dev/vhci" +#define VHCI_UDEV "/dev/hci_vhci" + +struct bt_vhci_s { + int fd; + struct HCIInfo *info; + + uint8_t hdr[4096]; + int len; +}; + +static void vhci_read(void *opaque) +{ + struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; + uint8_t *pkt; + int pktlen; + + /* Seems that we can't read only the header first and then the amount + * of data indicated in the header because Linux will discard everything + * that's not been read in one go. */ + s->len = read(s->fd, s->hdr, sizeof(s->hdr)); + + if (s->len < 0) { + fprintf(stderr, "qemu: error %i reading the PDU\n", errno); + return; + } + + pkt = s->hdr; + while (s->len --) + switch (*pkt ++) { + case HCI_COMMAND_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->info->cmd_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_ACLDATA_PKT: + if (s->len < 4) + goto bad_pkt; + + pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); + s->info->acl_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + case HCI_SCODATA_PKT: + if (s->len < 3) + goto bad_pkt; + + pktlen = MIN(pkt[2] + 3, s->len); + s->info->sco_send(s->info, pkt, pktlen); + s->len -= pktlen; + pkt += pktlen; + break; + + default: + bad_pkt: + fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); + } +} + +static void vhci_host_send(void *opaque, + int type, const uint8_t *data, int len) +{ + struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; +#if 0 + uint8_t pkt = type; + struct iovec iv[2]; + + iv[0].iov_base = &pkt; + iv[0].iov_len = 1; + iv[1].iov_base = (void *) data; + iv[1].iov_len = len; + + while (writev(s->fd, iv, 2) < 0) + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } +#else + /* Apparently VHCI wants us to write everything in one chunk :-( */ + static uint8_t buf[4096]; + + buf[0] = type; + memcpy(buf + 1, data, len); + + while (write(s->fd, buf, len + 1) < 0) + if (errno != EAGAIN && errno != EINTR) { + fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", + errno); + return; + } +#endif +} + +static void vhci_out_hci_packet_event(void *opaque, + const uint8_t *data, int len) +{ + vhci_host_send(opaque, HCI_EVENT_PKT, data, len); +} + +static void vhci_out_hci_packet_acl(void *opaque, + const uint8_t *data, int len) +{ + vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); +} + +void bt_vhci_init(struct HCIInfo *info) +{ + struct bt_vhci_s *s; + int err[2]; + int fd; + + fd = open(VHCI_DEV, O_RDWR); + err[0] = errno; + if (fd < 0) { + fd = open(VHCI_UDEV, O_RDWR); + err[1] = errno; + } + + if (fd < 0) { + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + VHCI_DEV, strerror(err[0]), err[0]); + fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", + VHCI_UDEV, strerror(err[1]), err[1]); + exit(-1); + } + + s = qemu_mallocz(sizeof(struct bt_vhci_s)); + s->fd = fd; + s->info = info ?: qemu_next_hci(); + s->info->opaque = s; + s->info->evt_recv = vhci_out_hci_packet_event; + s->info->acl_recv = vhci_out_hci_packet_acl; + + qemu_set_fd_handler(s->fd, vhci_read, 0, s); +} diff -Nur kvm-76/qemu/compatfd.c kvm-userspace/qemu/compatfd.c --- kvm-76/qemu/compatfd.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/compatfd.c 2008-10-12 09:38:23.000000000 +0200 @@ -33,35 +33,35 @@ sigprocmask(SIG_BLOCK, &all, NULL); do { - siginfo_t siginfo; + siginfo_t siginfo; - err = sigwaitinfo(&info->mask, &siginfo); - if (err == -1 && errno == EINTR) { + err = sigwaitinfo(&info->mask, &siginfo); + if (err == -1 && errno == EINTR) { err = 0; continue; } - if (err > 0) { - char buffer[128]; - size_t offset = 0; - - memcpy(buffer, &err, sizeof(err)); - while (offset < sizeof(buffer)) { - ssize_t len; - - len = write(info->fd, buffer + offset, - sizeof(buffer) - offset); - if (len == -1 && errno == EINTR) - continue; - - if (len <= 0) { - err = -1; - break; - } - - offset += len; - } - } + if (err > 0) { + char buffer[128]; + size_t offset = 0; + + memcpy(buffer, &err, sizeof(err)); + while (offset < sizeof(buffer)) { + ssize_t len; + + len = write(info->fd, buffer + offset, + sizeof(buffer) - offset); + if (len == -1 && errno == EINTR) + continue; + + if (len <= 0) { + err = -1; + break; + } + + offset += len; + } + } } while (err >= 0); return NULL; @@ -76,13 +76,13 @@ info = malloc(sizeof(*info)); if (info == NULL) { - errno = ENOMEM; - return -1; + errno = ENOMEM; + return -1; } if (pipe(fds) == -1) { - free(info); - return -1; + free(info); + return -1; } memcpy(&info->mask, mask, sizeof(*mask)); @@ -100,12 +100,12 @@ int qemu_signalfd(const sigset_t *mask) { -#if defined(SYS_signalfd) +#if defined(CONFIG_signalfd) int ret; ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); - if (!(ret == -1 && errno == ENOSYS)) - return ret; + if (ret != -1) + return ret; #endif return qemu_signalfd_compat(mask); @@ -113,15 +113,14 @@ int qemu_eventfd(int *fds) { -#if defined(SYS_eventfd) +#if defined(CONFIG_eventfd) int ret; ret = syscall(SYS_eventfd, 0); if (ret >= 0) { - fds[0] = fds[1] = ret; - return 0; - } else if (!(ret == -1 && errno == ENOSYS)) - return ret; + fds[0] = fds[1] = ret; + return 0; + } #endif return pipe(fds); diff -Nur kvm-76/qemu/configure kvm-userspace/qemu/configure --- kvm-76/qemu/configure 2008-09-28 20:38:17.000000000 +0200 +++ kvm-userspace/qemu/configure 2008-10-12 09:38:23.000000000 +0200 @@ -15,6 +15,7 @@ TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}" TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S" +TMPI="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.i" # default parameters prefix="" @@ -113,6 +114,9 @@ aio="yes" nptl="yes" mixemu="no" +bluez="yes" +signalfd="no" +eventfd="no" cpu_emulation="yes" device_tree_support="" @@ -309,6 +313,8 @@ ;; --disable-brlapi) brlapi="no" ;; + --disable-bluez) bluez="no" + ;; --enable-profiler) profiler="yes" ;; --kernel-path=*) kernel_path="$optarg" @@ -453,6 +459,7 @@ echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" +echo " --disable-bluez disable bluez stack connectivity" echo " --disable-nptl disable usermode NPTL support" echo " --enable-system enable all system emulation targets" echo " --disable-system disable all system emulation targets" @@ -510,8 +517,10 @@ linux_user="no" fi -if [ "$bsd" = "yes" -o "$darwin" = "yes" -o "$mingw32" = "yes" ] ; then +if [ "$darwin" = "yes" -o "$mingw32" = "yes" ] ; then AIOLIBS= +elif [ "$bsd" = "yes" ]; then + AIOLIBS="-lpthread" else # Some Linux architectures (e.g. s390) don't imply -lpthread automatically. AIOLIBS="-lrt -lpthread" @@ -693,17 +702,8 @@ # ppc specific hostlongbits selection if test "$cpu" = "powerpc" ; then - cat > $TMPC < /dev/null; then - $TMPE - case $? in - 4) hostlongbits="32";; - 8) hostlongbits="64";; - *) echo "Couldn't determine bits per long value"; exit 1;; - esac + if $cc $ARCH_CFLAGS -dM -E - -o $TMPI 2>/dev/null $TMPC << EOF +#include +int main(void) { return bt_error(0); } +EOF + bluez_cflags=`pkg-config --cflags bluez` + bluez_libs=`pkg-config --libs bluez` + if $cc $ARCH_CFLAGS -o $TMPE ${OS_FLAGS} $bluez_cflags $TMPC \ + $bluez_libs 2> /dev/null ; then + : + else + bluez="no" + fi +fi + +########################################## # AIO probe if test "$aio" = "yes" ; then aio=no @@ -943,6 +963,33 @@ fi fi +########################################## +# signalfd probe +cat > $TMPC << EOF +#define _GNU_SOURCE +#include +#include +#include +int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); } +EOF + +if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then + signalfd=yes +fi + +########################################## +# eventfd probe +cat > $TMPC << EOF +#define _GNU_SOURCE +#include +#include +int main(void) { return syscall(SYS_eventfd, 0); } +EOF + +if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then + eventfd=yes +fi + # Check if tools are available to build documentation. if [ -x "`which texi2html 2>/dev/null`" ] && \ [ -x "`which pod2man 2>/dev/null`" ]; then @@ -1255,7 +1302,7 @@ qemu_version=`head $source_path/VERSION` echo "VERSION=$qemu_version" >>$config_mak echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h -echo "#define KVM_VERSION \"kvm-76\"" >> $config_h +echo "#define KVM_VERSION \"kvm-devel\"" >> $config_h echo "SRC_PATH=$source_path" >> $config_mak if [ "$source_path_used" = "yes" ]; then @@ -1298,10 +1345,22 @@ echo "#define CONFIG_BRLAPI 1" >> $config_h echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak fi +if test "$bluez" = "yes" ; then + echo "CONFIG_BLUEZ=yes" >> $config_mak + echo "CONFIG_BLUEZ_CFLAGS=$bluez_cflags" >> $config_mak + echo "CONFIG_BLUEZ_LIBS=$bluez_libs" >> $config_mak + echo "#define CONFIG_BLUEZ 1" >> $config_h +fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h echo "CONFIG_AIO=yes" >> $config_mak fi +if test "$signalfd" = "yes" ; then + echo "#define CONFIG_signalfd 1" >> $config_h +fi +if test "$eventfd" = "yes" ; then + echo "#define CONFIG_eventfd 1" >> $config_h +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -1462,10 +1521,8 @@ ;; alpha) echo "TARGET_ARCH=alpha" >> $config_mak - echo "CONFIG_DYNGEN_OP=yes" >> $config_mak echo "#define TARGET_ARCH \"alpha\"" >> $config_h echo "#define TARGET_ALPHA 1" >> $config_h - echo "#define CONFIG_DYNGEN_OP 1" >> $config_h ;; arm|armeb) echo "TARGET_ARCH=arm" >> $config_mak @@ -1648,4 +1705,4 @@ done fi -rm -f $TMPO $TMPC $TMPE $TMPS +rm -f $TMPO $TMPC $TMPE $TMPS $TMPI diff -Nur kvm-76/qemu/cpu-all.h kvm-userspace/qemu/cpu-all.h --- kvm-76/qemu/cpu-all.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/cpu-all.h 2008-10-12 09:38:23.000000000 +0200 @@ -797,7 +797,7 @@ const char *help; } CPULogItem; -extern CPULogItem cpu_log_items[]; +extern const CPULogItem cpu_log_items[]; void cpu_set_log(int log_flags); void cpu_set_log_filename(const char *filename); diff -Nur kvm-76/qemu/curses_keys.h kvm-userspace/qemu/curses_keys.h --- kvm-76/qemu/curses_keys.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/curses_keys.h 2008-10-12 09:38:23.000000000 +0200 @@ -37,7 +37,7 @@ #define CURSES_KEYS KEY_MAX /* KEY_MAX defined in */ -int curses2keycode[CURSES_KEYS] = { +static const int curses2keycode[CURSES_KEYS] = { [0 ... (CURSES_KEYS - 1)] = -1, [0x01b] = 1, /* Escape */ @@ -216,7 +216,7 @@ }; -int curses2keysym[CURSES_KEYS] = { +static const int curses2keysym[CURSES_KEYS] = { [0 ... (CURSES_KEYS - 1)] = -1, ['\n'] = '\n', @@ -244,7 +244,7 @@ int keysym; } name2keysym_t; -static name2keysym_t name2keysym[] = { +static const name2keysym_t name2keysym[] = { /* Plain ASCII */ { "space", 0x020 }, { "exclam", 0x021 }, diff -Nur kvm-76/qemu/darwin-user/qemu.h kvm-userspace/qemu/darwin-user/qemu.h --- kvm-76/qemu/darwin-user/qemu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/darwin-user/qemu.h 2008-10-12 09:38:23.000000000 +0200 @@ -109,8 +109,7 @@ void init_paths(const char *prefix); const char *path(const char *pathname); -extern int loglevel; -extern FILE *logfile; +#include "qemu-log.h" /* commpage.c */ void commpage_init(void); diff -Nur kvm-76/qemu/darwin-user/syscall.c kvm-userspace/qemu/darwin-user/syscall.c --- kvm-76/qemu/darwin-user/syscall.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/darwin-user/syscall.c 2008-10-12 09:38:23.000000000 +0200 @@ -654,7 +654,7 @@ #define MAX_STRUCT_SIZE 4096 -IOCTLEntry ioctl_entries[] = { +static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, types...) \ { cmd, cmd, #cmd, access, { types } }, #include "ioctls.h" diff -Nur kvm-76/qemu/exec.c kvm-userspace/qemu/exec.c --- kvm-76/qemu/exec.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/exec.c 2008-10-12 09:38:23.000000000 +0200 @@ -89,10 +89,10 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif -TranslationBlock *tbs; +static TranslationBlock *tbs; int code_gen_max_blocks; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -int nb_tbs; +static int nb_tbs; /* any access to the tbs or the page table must use this lock */ spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; @@ -109,10 +109,10 @@ #endif uint8_t code_gen_prologue[1024] code_gen_section; -uint8_t *code_gen_buffer; -unsigned long code_gen_buffer_size; +static uint8_t *code_gen_buffer; +static unsigned long code_gen_buffer_size; /* threshold to flush the translated code buffer */ -unsigned long code_gen_buffer_max_size; +static unsigned long code_gen_buffer_max_size; uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) @@ -175,7 +175,7 @@ /* XXX: for system emulation, it could just be an array */ static PageDesc *l1_map[L1_SIZE]; -PhysPageDesc **l1_phys_map; +static PhysPageDesc **l1_phys_map; #if !defined(CONFIG_USER_ONLY) static void io_mem_init(void); @@ -189,7 +189,7 @@ #endif /* log support */ -const char *logfilename = "/tmp/qemu.log"; +static const char *logfilename = "/tmp/qemu.log"; FILE *logfile; int loglevel; static int log_append = 0; @@ -455,6 +455,28 @@ exit(1); } } +#elif defined(__FreeBSD__) + { + int flags; + void *addr = NULL; + flags = MAP_PRIVATE | MAP_ANONYMOUS; +#if defined(__x86_64__) + /* FreeBSD doesn't have MAP_32BIT, use MAP_FIXED and assume + * 0x40000000 is free */ + flags |= MAP_FIXED; + addr = (void *)0x40000000; + /* Cannot map more than that */ + if (code_gen_buffer_size > (800 * 1024 * 1024)) + code_gen_buffer_size = (800 * 1024 * 1024); +#endif + code_gen_buffer = mmap(addr, code_gen_buffer_size, + PROT_WRITE | PROT_READ | PROT_EXEC, + flags, -1, 0); + if (code_gen_buffer == MAP_FAILED) { + fprintf(stderr, "Could not allocate dynamic translator buffer\n"); + exit(1); + } + } #else code_gen_buffer = qemu_malloc(code_gen_buffer_size); if (!code_gen_buffer) { @@ -632,7 +654,7 @@ } } -void tb_jmp_check(TranslationBlock *tb) +static void tb_jmp_check(TranslationBlock *tb) { TranslationBlock *tb1; unsigned int n1; @@ -1516,7 +1538,7 @@ env->interrupt_request &= ~mask; } -CPULogItem cpu_log_items[] = { +const CPULogItem cpu_log_items[] = { { CPU_LOG_TB_OUT_ASM, "out_asm", "show generated host assembly code for each compiled TB" }, { CPU_LOG_TB_IN_ASM, "in_asm", @@ -1556,7 +1578,7 @@ /* takes a comma separated list of log masks. Return 0 if error. */ int cpu_str_to_log_mask(const char *str) { - CPULogItem *item; + const CPULogItem *item; int mask; const char *p, *p1; @@ -2296,7 +2318,7 @@ { ram_addr_t addr; if ((phys_ram_alloc_offset + size) > phys_ram_size) { - fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 "\n", + fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 ")\n", (uint64_t)size, (uint64_t)phys_ram_size); abort(); } diff -Nur kvm-76/qemu/hw/acpi.c kvm-userspace/qemu/hw/acpi.c --- kvm-76/qemu/hw/acpi.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/acpi.c 2008-10-12 09:38:23.000000000 +0200 @@ -726,7 +726,11 @@ { CPUState *env; - if ((state) && (!qemu_kvm_cpu_env(cpu))) { + if (state +#ifdef USE_KVM + && (!qemu_kvm_cpu_env(cpu)) +#endif + ) { env = pc_new_cpu(cpu, model, 1); if (!env) { fprintf(stderr, "cpu %d creation failed\n", cpu); diff -Nur kvm-76/qemu/hw/apic.c kvm-userspace/qemu/hw/apic.c --- kvm-76/qemu/hw/apic.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/apic.c 2008-10-12 09:38:23.000000000 +0200 @@ -114,7 +114,7 @@ { unsigned int ret = 0; -#if defined(HOST_I386) +#if defined(HOST_I386) || defined(HOST_X86_64) __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); return ret; #else @@ -135,7 +135,7 @@ { unsigned int ret = 0; -#if defined(HOST_I386) +#if defined(HOST_I386) || defined(HOST_X86_64) __asm__ __volatile__ ("bsf %1, %0\n" : "+r" (ret) : "rm" (value)); return ret; #else @@ -467,6 +467,11 @@ s->initial_count_load_time = 0; s->next_time = 0; + cpu_reset(s->cpu_env); + + if (!(s->apicbase & MSR_IA32_APICBASE_BSP)) + s->cpu_env->halted = 1; + if (kvm_enabled() && !qemu_kvm_irqchip_in_kernel()) if (s->cpu_env) kvm_apic_init(s->cpu_env); diff -Nur kvm-76/qemu/hw/boards.h kvm-userspace/qemu/hw/boards.h --- kvm-76/qemu/hw/boards.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/boards.h 2008-10-12 09:38:23.000000000 +0200 @@ -17,6 +17,7 @@ #define RAMSIZE_FIXED (1 << 0) ram_addr_t ram_require; int nodisk_ok; + int use_scsi; struct QEMUMachine *next; } QEMUMachine; @@ -69,6 +70,7 @@ /* sun4u.c */ extern QEMUMachine sun4u_machine; extern QEMUMachine sun4v_machine; +extern QEMUMachine niagara_machine; /* integratorcp.c */ extern QEMUMachine integratorcp_machine; diff -Nur kvm-76/qemu/hw/bt-hci-csr.c kvm-userspace/qemu/hw/bt-hci-csr.c --- kvm-76/qemu/hw/bt-hci-csr.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt-hci-csr.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,456 @@ +/* + * Bluetooth serial HCI transport. + * CSR41814 HCI with H4p vendor extensions. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "qemu-char.h" +#include "qemu-timer.h" +#include "irq.h" +#include "sysemu.h" +#include "net.h" +#include "bt.h" + +struct csrhci_s { + int enable; + qemu_irq *pins; + int pin_state; + int modem_state; + CharDriverState chr; +#define FIFO_LEN 4096 + int out_start; + int out_len; + int out_size; + uint8_t outfifo[FIFO_LEN * 2]; + uint8_t inpkt[FIFO_LEN]; + int in_len; + int in_hdr; + int in_data; + QEMUTimer *out_tm; + int64_t baud_delay; + + bdaddr_t bd_addr; + struct HCIInfo *hci; +}; + +/* H4+ packet types */ +enum { + H4_CMD_PKT = 1, + H4_ACL_PKT = 2, + H4_SCO_PKT = 3, + H4_EVT_PKT = 4, + H4_NEG_PKT = 6, + H4_ALIVE_PKT = 7, +}; + +/* CSR41814 negotiation start magic packet */ +static const uint8_t csrhci_neg_packet[] = { + H4_NEG_PKT, 10, + 0x00, 0xa0, 0x01, 0x00, 0x00, + 0x4c, 0x00, 0x96, 0x00, 0x00, +}; + +/* CSR41814 vendor-specific command OCFs */ +enum { + OCF_CSR_SEND_FIRMWARE = 0x000, +}; + +static inline void csrhci_fifo_wake(struct csrhci_s *s) +{ + if (!s->enable || !s->out_len) + return; + + /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ + if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) && + s->chr.chr_read) { + s->chr.chr_read(s->chr.handler_opaque, + s->outfifo + s->out_start ++, 1); + s->out_len --; + if (s->out_start >= s->out_size) { + s->out_start = 0; + s->out_size = FIFO_LEN; + } + } + + if (s->out_len) + qemu_mod_timer(s->out_tm, qemu_get_clock(vm_clock) + s->baud_delay); +} + +#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) +static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len) +{ + int off = s->out_start + s->out_len; + + /* TODO: do the padding here, i.e. align len */ + s->out_len += len; + + if (off < FIFO_LEN) { + if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) { + fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); + exit(-1); + } + return s->outfifo + off; + } + + if (s->out_len > s->out_size) { + fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); + exit(-1); + } + + return s->outfifo + off - s->out_size; +} + +static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s, + int type, int len) +{ + uint8_t *ret = csrhci_out_packetz(s, len + 2); + + *ret ++ = type; + *ret ++ = len; + + return ret; +} + +static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s, + int evt, int len) +{ + uint8_t *ret = csrhci_out_packetz(s, + len + 1 + sizeof(struct hci_event_hdr)); + + *ret ++ = H4_EVT_PKT; + ((struct hci_event_hdr *) ret)->evt = evt; + ((struct hci_event_hdr *) ret)->plen = len; + + return ret + sizeof(struct hci_event_hdr); +} + +static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf, + uint8_t *data, int len) +{ + int offset; + uint8_t *rpkt; + + switch (ocf) { + case OCF_CSR_SEND_FIRMWARE: + /* Check if this is the bd_address packet */ + if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) { + offset = 18; + s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */ + s->bd_addr.b[1] = data[offset + 6]; + s->bd_addr.b[2] = data[offset + 4]; + s->bd_addr.b[3] = data[offset + 0]; + s->bd_addr.b[4] = data[offset + 3]; + s->bd_addr.b[5] = data[offset + 2]; + + s->hci->bdaddr_set(s->hci, s->bd_addr.b); + fprintf(stderr, "%s: bd_address loaded from firmware: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, + s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2], + s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]); + } + + rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11); + /* Status bytes: no error */ + rpkt[9] = 0x00; + rpkt[10] = 0x00; + break; + + default: + fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__); + return; + } + + csrhci_fifo_wake(s); +} + +static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt) +{ + uint8_t *rpkt; + int opc; + + switch (*pkt ++) { + case H4_CMD_PKT: + opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode); + if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) { + csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc), + pkt + sizeof(struct hci_command_hdr), + s->in_len - sizeof(struct hci_command_hdr) - 1); + return; + } + + /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes, + * we need to send it to the HCI layer and then add our supported + * commands to the returned mask (such as OGF_VENDOR_CMD). With + * bt-hci.c we could just have hooks for this kind of commands but + * we can't with bt-host.c. */ + + s->hci->cmd_send(s->hci, pkt, s->in_len - 1); + break; + + case H4_EVT_PKT: + goto bad_pkt; + + case H4_ACL_PKT: + s->hci->acl_send(s->hci, pkt, s->in_len - 1); + break; + + case H4_SCO_PKT: + s->hci->sco_send(s->hci, pkt, s->in_len - 1); + break; + + case H4_NEG_PKT: + if (s->in_hdr != sizeof(csrhci_neg_packet) || + memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) { + fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__); + return; + } + pkt += 2; + + rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10); + + *rpkt ++ = 0x20; /* Operational settings negotation Ok */ + memcpy(rpkt, pkt, 7); rpkt += 7; + *rpkt ++ = 0xff; + *rpkt ++ = 0xff; + break; + + case H4_ALIVE_PKT: + if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) { + fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__); + return; + } + + rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2); + + *rpkt ++ = 0xcc; + *rpkt ++ = 0x00; + break; + + default: + bad_pkt: + /* TODO: error out */ + fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__); + break; + } + + csrhci_fifo_wake(s); +} + +static int csrhci_header_len(const uint8_t *pkt) +{ + switch (pkt[0]) { + case H4_CMD_PKT: + return HCI_COMMAND_HDR_SIZE; + case H4_EVT_PKT: + return HCI_EVENT_HDR_SIZE; + case H4_ACL_PKT: + return HCI_ACL_HDR_SIZE; + case H4_SCO_PKT: + return HCI_SCO_HDR_SIZE; + case H4_NEG_PKT: + return pkt[1] + 1; + case H4_ALIVE_PKT: + return 3; + } + + exit(-1); +} + +static int csrhci_data_len(const uint8_t *pkt) +{ + switch (*pkt ++) { + case H4_CMD_PKT: + /* It seems that vendor-specific command packets for H4+ are all + * one byte longer than indicated in the standard header. */ + if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00) + return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1; + + return ((struct hci_command_hdr *) pkt)->plen; + case H4_EVT_PKT: + return ((struct hci_event_hdr *) pkt)->plen; + case H4_ACL_PKT: + return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen); + case H4_SCO_PKT: + return ((struct hci_sco_hdr *) pkt)->dlen; + case H4_NEG_PKT: + case H4_ALIVE_PKT: + return 0; + } + + exit(-1); +} + +static int csrhci_write(struct CharDriverState *chr, + const uint8_t *buf, int len) +{ + struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + int plen = s->in_len; + + if (!s->enable) + return 0; + + s->in_len += len; + memcpy(s->inpkt + plen, buf, len); + + while (1) { + if (s->in_len >= 2 && plen < 2) + s->in_hdr = csrhci_header_len(s->inpkt) + 1; + + if (s->in_len >= s->in_hdr && plen < s->in_hdr) + s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr; + + if (s->in_len >= s->in_data) { + csrhci_in_packet(s, s->inpkt); + + memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data); + s->in_len -= s->in_data; + s->in_hdr = INT_MAX; + s->in_data = INT_MAX; + plen = 0; + } else + break; + } + + return len; +} + +static void csrhci_out_hci_packet_event(void *opaque, + const uint8_t *data, int len) +{ + struct csrhci_s *s = (struct csrhci_s *) opaque; + uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ + + *pkt ++ = H4_EVT_PKT; + memcpy(pkt, data, len); + + csrhci_fifo_wake(s); +} + +static void csrhci_out_hci_packet_acl(void *opaque, + const uint8_t *data, int len) +{ + struct csrhci_s *s = (struct csrhci_s *) opaque; + uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ + + *pkt ++ = H4_ACL_PKT; + pkt[len & ~1] = 0; + memcpy(pkt, data, len); + + csrhci_fifo_wake(s); +} + +static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg) +{ + QEMUSerialSetParams *ssp; + struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + int prev_state = s->modem_state; + + switch (cmd) { + case CHR_IOCTL_SERIAL_SET_PARAMS: + ssp = (QEMUSerialSetParams *) arg; + s->baud_delay = ticks_per_sec / ssp->speed; + /* Moments later... (but shorter than 100ms) */ + s->modem_state |= CHR_TIOCM_CTS; + break; + + case CHR_IOCTL_SERIAL_GET_TIOCM: + *(int *) arg = s->modem_state; + break; + + case CHR_IOCTL_SERIAL_SET_TIOCM: + s->modem_state = *(int *) arg; + if (~s->modem_state & prev_state & CHR_TIOCM_RTS) + s->modem_state &= ~CHR_TIOCM_CTS; + break; + + default: + return -ENOTSUP; + } + return 0; +} + +static void csrhci_reset(struct csrhci_s *s) +{ + s->out_len = 0; + s->out_size = FIFO_LEN; + s->in_len = 0; + s->baud_delay = ticks_per_sec; + s->enable = 0; + s->in_hdr = INT_MAX; + s->in_data = INT_MAX; + + s->modem_state = 0; + /* After a while... (but sooner than 10ms) */ + s->modem_state |= CHR_TIOCM_CTS; + + memset(&s->bd_addr, 0, sizeof(bdaddr_t)); +} + +static void csrhci_out_tick(void *opaque) +{ + csrhci_fifo_wake((struct csrhci_s *) opaque); +} + +static void csrhci_pins(void *opaque, int line, int level) +{ + struct csrhci_s *s = (struct csrhci_s *) opaque; + int state = s->pin_state; + + s->pin_state &= ~(1 << line); + s->pin_state |= (!!level) << line; + + if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) { + /* TODO: Disappear from lower layers */ + csrhci_reset(s); + } + + if (s->pin_state == 3 && state != 3) { + s->enable = 1; + /* TODO: Wake lower layers up */ + } +} + +qemu_irq *csrhci_pins_get(CharDriverState *chr) +{ + struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + + return s->pins; +} + +CharDriverState *uart_hci_init(qemu_irq wakeup) +{ + struct csrhci_s *s = (struct csrhci_s *) + qemu_mallocz(sizeof(struct csrhci_s)); + + s->chr.opaque = s; + s->chr.chr_write = csrhci_write; + s->chr.chr_ioctl = csrhci_ioctl; + + s->hci = qemu_next_hci(); + s->hci->opaque = s; + s->hci->evt_recv = csrhci_out_hci_packet_event; + s->hci->acl_recv = csrhci_out_hci_packet_acl; + + s->out_tm = qemu_new_timer(vm_clock, csrhci_out_tick, s); + s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); + csrhci_reset(s); + + return &s->chr; +} diff -Nur kvm-76/qemu/hw/bt-hci.c kvm-userspace/qemu/hw/bt-hci.c --- kvm-76/qemu/hw/bt-hci.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt-hci.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,2220 @@ +/* + * QEMU Bluetooth HCI logic. + * + * Copyright (C) 2007 OpenMoko, Inc. + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "qemu-common.h" +#include "qemu-timer.h" +#include "usb.h" +#include "net.h" +#include "bt.h" + +struct bt_hci_s { + uint8_t *(*evt_packet)(void *opaque); + void (*evt_submit)(void *opaque, int len); + void *opaque; + uint8_t evt_buf[256]; + + uint8_t acl_buf[4096]; + int acl_len; + + uint16_t asb_handle; + uint16_t psb_handle; + + int last_cmd; /* Note: Always little-endian */ + + struct bt_device_s *conn_req_host; + + struct { + int inquire; + int periodic; + int responses_left; + int responses; + QEMUTimer *inquiry_done; + QEMUTimer *inquiry_next; + int inquiry_length; + int inquiry_period; + int inquiry_mode; + +#define HCI_HANDLE_OFFSET 0x20 +#define HCI_HANDLES_MAX 0x10 + struct bt_hci_master_link_s { + struct bt_link_s *link; + void (*lmp_acl_data)(struct bt_link_s *link, + const uint8_t *data, int start, int len); + QEMUTimer *acl_mode_timer; + } handle[HCI_HANDLES_MAX]; + uint32_t role_bmp; + int last_handle; + int connecting; + bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX]; + } lm; + + uint8_t event_mask[8]; + uint16_t voice_setting; /* Notw: Always little-endian */ + uint16_t conn_accept_tout; + QEMUTimer *conn_accept_timer; + + struct HCIInfo info; + struct bt_device_s device; +}; + +#define DEFAULT_RSSI_DBM 20 + +#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info) +#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device) + +struct bt_hci_link_s { + struct bt_link_s btlink; + uint16_t handle; /* Local */ +}; + +/* LMP layer emulation */ +static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data) +{ + int resp, resplen, error, op, tr; + uint8_t respdata[17]; + + if (length < 1) + return; + + tr = *data & 1; + op = *(data ++) >> 1; + resp = LMP_ACCEPTED; + resplen = 2; + respdata[1] = op; + error = 0; + length --; + + if (op >= 0x7c) { /* Extended opcode */ + op |= *(data ++) << 8; + resp = LMP_ACCEPTED_EXT; + resplen = 4; + respdata[0] = op >> 8; + respdata[1] = op & 0xff; + length --; + } + + switch (op) { + case LMP_ACCEPTED: + /* data[0] Op code + */ + if (length < 1) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + case LMP_ACCEPTED_EXT: + /* data[0] Escape op code + * data[1] Extended op code + */ + if (length < 2) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + case LMP_NOT_ACCEPTED: + /* data[0] Op code + * data[1] Error code + */ + if (length < 2) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + case LMP_NOT_ACCEPTED_EXT: + /* data[0] Op code + * data[1] Extended op code + * data[2] Error code + */ + if (length < 3) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + case LMP_HOST_CONNECTION_REQ: + break; + + case LMP_SETUP_COMPLETE: + resp = LMP_SETUP_COMPLETE; + resplen = 1; + bt->setup = 1; + break; + + case LMP_DETACH: + /* data[0] Error code + */ + if (length < 1) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + bt->setup = 0; + resp = 0; + break; + + case LMP_SUPERVISION_TIMEOUT: + /* data[0,1] Supervision timeout + */ + if (length < 2) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + case LMP_QUALITY_OF_SERVICE: + resp = 0; + /* Fall through */ + case LMP_QOS_REQ: + /* data[0,1] Poll interval + * data[2] N(BC) + */ + if (length < 3) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + break; + + case LMP_MAX_SLOT: + resp = 0; + /* Fall through */ + case LMP_MAX_SLOT_REQ: + /* data[0] Max slots + */ + if (length < 1) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + break; + + case LMP_AU_RAND: + case LMP_IN_RAND: + case LMP_COMB_KEY: + /* data[0-15] Random number + */ + if (length < 16) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + if (op == LMP_AU_RAND) { + if (bt->key_present) { + resp = LMP_SRES; + resplen = 5; + /* XXX: [Part H] Section 6.1 on page 801 */ + } else { + error = HCI_PIN_OR_KEY_MISSING; + goto not_accepted; + } + } else if (op == LMP_IN_RAND) { + error = HCI_PAIRING_NOT_ALLOWED; + goto not_accepted; + } else { + /* XXX: [Part H] Section 3.2 on page 779 */ + resp = LMP_UNIT_KEY; + resplen = 17; + memcpy(respdata + 1, bt->key, 16); + + error = HCI_UNIT_LINK_KEY_USED; + goto not_accepted; + } + break; + + case LMP_UNIT_KEY: + /* data[0-15] Key + */ + if (length < 16) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + memcpy(bt->key, data, 16); + bt->key_present = 1; + break; + + case LMP_SRES: + /* data[0-3] Authentication response + */ + if (length < 4) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + break; + + case LMP_CLKOFFSET_REQ: + resp = LMP_CLKOFFSET_RES; + resplen = 3; + respdata[1] = 0x33; + respdata[2] = 0x33; + break; + + case LMP_CLKOFFSET_RES: + /* data[0,1] Clock offset + * (Slave to master only) + */ + if (length < 2) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + break; + + case LMP_VERSION_REQ: + case LMP_VERSION_RES: + /* data[0] VersNr + * data[1,2] CompId + * data[3,4] SubVersNr + */ + if (length < 5) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + if (op == LMP_VERSION_REQ) { + resp = LMP_VERSION_RES; + resplen = 6; + respdata[1] = 0x20; + respdata[2] = 0xff; + respdata[3] = 0xff; + respdata[4] = 0xff; + respdata[5] = 0xff; + } else + resp = 0; + break; + + case LMP_FEATURES_REQ: + case LMP_FEATURES_RES: + /* data[0-7] Features + */ + if (length < 8) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + if (op == LMP_FEATURES_REQ) { + resp = LMP_FEATURES_RES; + resplen = 9; + respdata[1] = (bt->lmp_caps >> 0) & 0xff; + respdata[2] = (bt->lmp_caps >> 8) & 0xff; + respdata[3] = (bt->lmp_caps >> 16) & 0xff; + respdata[4] = (bt->lmp_caps >> 24) & 0xff; + respdata[5] = (bt->lmp_caps >> 32) & 0xff; + respdata[6] = (bt->lmp_caps >> 40) & 0xff; + respdata[7] = (bt->lmp_caps >> 48) & 0xff; + respdata[8] = (bt->lmp_caps >> 56) & 0xff; + } else + resp = 0; + break; + + case LMP_NAME_REQ: + /* data[0] Name offset + */ + if (length < 1) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = LMP_NAME_RES; + resplen = 17; + respdata[1] = data[0]; + respdata[2] = strlen(bt->lmp_name); + memset(respdata + 3, 0x00, 14); + if (respdata[2] > respdata[1]) + memcpy(respdata + 3, bt->lmp_name + respdata[1], + respdata[2] - respdata[1]); + break; + + case LMP_NAME_RES: + /* data[0] Name offset + * data[1] Name length + * data[2-15] Name fragment + */ + if (length < 16) { + error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; + goto not_accepted; + } + resp = 0; + break; + + default: + error = HCI_UNKNOWN_LMP_PDU; + /* Fall through */ + not_accepted: + if (op >> 8) { + resp = LMP_NOT_ACCEPTED_EXT; + resplen = 5; + respdata[0] = op >> 8; + respdata[1] = op & 0xff; + respdata[2] = error; + } else { + resp = LMP_NOT_ACCEPTED; + resplen = 3; + respdata[0] = op & 0xff; + respdata[1] = error; + } + } + + if (resp == 0) + return; + + if (resp >> 8) { + respdata[0] = resp >> 8; + respdata[1] = resp & 0xff; + } else + respdata[0] = resp & 0xff; + + respdata[0] <<= 1; + respdata[0] |= tr; +} + +void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data) +{ + struct bt_device_s *slave; + if (length < 1) + return; + + slave = 0; +#if 0 + slave = net->slave; +#endif + + switch (data[0] & 3) { + case LLID_ACLC: + bt_submit_lmp(slave, length - 1, data + 1); + break; + case LLID_ACLU_START: +#if 0 + bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1); + breka; +#endif + default: + case LLID_ACLU_CONT: + break; + } +} + +/* HCI layer emulation */ + +/* Note: we could ignore endiannes because unswapped handles will still + * be valid as connection identifiers for the guest - they don't have to + * be continuously allocated. We do it though, to preserve similar + * behaviour between hosts. Some things, like the BD_ADDR cannot be + * preserved though (for example if a real hci is used). */ +#ifdef WORDS_BIGENDIAN +# define HNDL(raw) bswap16(raw) +#else +# define HNDL(raw) (raw) +#endif + +static const uint8_t bt_event_reserved_mask[8] = { + 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, +}; + +static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci, + int evt, int len) +{ + uint8_t *packet, mask; + int mask_byte; + + if (len > 255) { + fprintf(stderr, "%s: HCI event params too long (%ib)\n", + __FUNCTION__, len); + exit(-1); + } + + mask_byte = (evt - 1) >> 3; + mask = 1 << ((evt - 1) & 3); + if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte]) + return 0; + + packet = hci->evt_packet(hci->opaque); + packet[0] = evt; + packet[1] = len; + + return &packet[2]; +} + +static inline void bt_hci_event(struct bt_hci_s *hci, int evt, + void *params, int len) +{ + uint8_t *packet = bt_hci_event_start(hci, evt, len); + + if (!packet) + return; + + if (len) + memcpy(packet, params, len); + + hci->evt_submit(hci->opaque, len + 2); +} + +static inline void bt_hci_event_status(struct bt_hci_s *hci, int status) +{ + evt_cmd_status params = { + .status = status, + .ncmd = 1, + .opcode = hci->last_cmd, + }; + + bt_hci_event(hci, EVT_CMD_STATUS, ¶ms, EVT_CMD_STATUS_SIZE); +} + +static inline void bt_hci_event_complete(struct bt_hci_s *hci, + void *ret, int len) +{ + uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE, + len + EVT_CMD_COMPLETE_SIZE); + evt_cmd_complete *params = (evt_cmd_complete *) packet; + + if (!packet) + return; + + params->ncmd = 1; + params->opcode = hci->last_cmd; + if (len) + memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len); + + hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2); +} + +static void bt_hci_inquiry_done(void *opaque) +{ + struct bt_hci_s *hci = (struct bt_hci_s *) opaque; + uint8_t status = HCI_SUCCESS; + + if (!hci->lm.periodic) + hci->lm.inquire = 0; + + /* The specification is inconsistent about this one. Page 565 reads + * "The event parameters of Inquiry Complete event will have a summary + * of the result from the Inquiry process, which reports the number of + * nearby Bluetooth devices that responded [so hci->responses].", but + * Event Parameters (see page 729) has only Status. */ + bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1); +} + +static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci, + struct bt_device_s *slave) +{ + inquiry_info params = { + .num_responses = 1, + .bdaddr = BAINIT(&slave->bd_addr), + .pscan_rep_mode = 0x00, /* R0 */ + .pscan_period_mode = 0x00, /* P0 - deprecated */ + .pscan_mode = 0x00, /* Standard scan - deprecated */ + .dev_class[0] = slave->class[0], + .dev_class[1] = slave->class[1], + .dev_class[2] = slave->class[2], + /* TODO: return the clkoff *differenece* */ + .clock_offset = slave->clkoff, /* Note: no swapping */ + }; + + bt_hci_event(hci, EVT_INQUIRY_RESULT, ¶ms, INQUIRY_INFO_SIZE); +} + +static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci, + struct bt_device_s *slave) +{ + inquiry_info_with_rssi params = { + .num_responses = 1, + .bdaddr = BAINIT(&slave->bd_addr), + .pscan_rep_mode = 0x00, /* R0 */ + .pscan_period_mode = 0x00, /* P0 - deprecated */ + .dev_class[0] = slave->class[0], + .dev_class[1] = slave->class[1], + .dev_class[2] = slave->class[2], + /* TODO: return the clkoff *differenece* */ + .clock_offset = slave->clkoff, /* Note: no swapping */ + .rssi = DEFAULT_RSSI_DBM, + }; + + bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI, + ¶ms, INQUIRY_INFO_WITH_RSSI_SIZE); +} + +static void bt_hci_inquiry_result(struct bt_hci_s *hci, + struct bt_device_s *slave) +{ + if (!slave->inquiry_scan || !hci->lm.responses_left) + return; + + hci->lm.responses_left --; + hci->lm.responses ++; + + switch (hci->lm.inquiry_mode) { + case 0x00: + return bt_hci_inquiry_result_standard(hci, slave); + case 0x01: + return bt_hci_inquiry_result_with_rssi(hci, slave); + default: + fprintf(stderr, "%s: bad inquiry mode %02x\n", __FUNCTION__, + hci->lm.inquiry_mode); + exit(-1); + } +} + +static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) +{ + qemu_mod_timer(timer, qemu_get_clock(vm_clock) + + muldiv64(period << 7, ticks_per_sec, 100)); +} + +static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length) +{ + struct bt_device_s *slave; + + hci->lm.inquiry_length = length; + for (slave = hci->device.net->slave; slave; slave = slave->next) + /* Don't uncover ourselves. */ + if (slave != &hci->device) + bt_hci_inquiry_result(hci, slave); + + /* TODO: register for a callback on a new device's addition to the + * scatternet so that if it's added before inquiry_length expires, + * an Inquiry Result is generated immediately. Alternatively re-loop + * through the devices on the inquiry_length expiration and report + * devices not seen before. */ + if (hci->lm.responses_left) + bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length); + else + bt_hci_inquiry_done(hci); + + if (hci->lm.periodic) + bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period); +} + +static void bt_hci_inquiry_next(void *opaque) +{ + struct bt_hci_s *hci = (struct bt_hci_s *) opaque; + + hci->lm.responses_left += hci->lm.responses; + hci->lm.responses = 0; + bt_hci_inquiry_start(hci, hci->lm.inquiry_length); +} + +static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle) +{ + return !(handle & HCI_HANDLE_OFFSET) || + handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) || + !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; +} + +static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle) +{ + return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET))); +} + +static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci, + uint16_t handle) +{ + struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; + + return bt_hci_role_master(hci, handle) ? link->slave : link->host; +} + +static void bt_hci_mode_tick(void *opaque); +static void bt_hci_lmp_link_establish(struct bt_hci_s *hci, + struct bt_link_s *link, int master) +{ + hci->lm.handle[hci->lm.last_handle].link = link; + + if (master) { + /* We are the master side of an ACL link */ + hci->lm.role_bmp |= 1 << hci->lm.last_handle; + + hci->lm.handle[hci->lm.last_handle].lmp_acl_data = + link->slave->lmp_acl_data; + } else { + /* We are the slave side of an ACL link */ + hci->lm.role_bmp &= ~(1 << hci->lm.last_handle); + + hci->lm.handle[hci->lm.last_handle].lmp_acl_data = + link->host->lmp_acl_resp; + } + + /* Mode */ + if (master) { + link->acl_mode = acl_active; + hci->lm.handle[hci->lm.last_handle].acl_mode_timer = + qemu_new_timer(vm_clock, bt_hci_mode_tick, link); + } +} + +static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle) +{ + handle &= ~HCI_HANDLE_OFFSET; + hci->lm.handle[handle].link = 0; + + if (bt_hci_role_master(hci, handle)) { + qemu_del_timer(hci->lm.handle[handle].acl_mode_timer); + qemu_free_timer(hci->lm.handle[handle].acl_mode_timer); + } +} + +static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr) +{ + struct bt_device_s *slave; + struct bt_link_s link; + + for (slave = hci->device.net->slave; slave; slave = slave->next) + if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) + break; + if (!slave || slave == &hci->device) + return -ENODEV; + + bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr); + + link.slave = slave; + link.host = &hci->device; + link.slave->lmp_connection_request(&link); /* Always last */ + + return 0; +} + +static void bt_hci_connection_reject(struct bt_hci_s *hci, + struct bt_device_s *host, uint8_t because) +{ + struct bt_link_s link = { + .slave = &hci->device, + .host = host, + /* Rest uninitialised */ + }; + + host->reject_reason = because; + host->lmp_connection_complete(&link); +} + +static void bt_hci_connection_reject_event(struct bt_hci_s *hci, + bdaddr_t *bdaddr) +{ + evt_conn_complete params; + + params.status = HCI_NO_CONNECTION; + params.handle = 0; + bacpy(¶ms.bdaddr, bdaddr); + params.link_type = ACL_LINK; + params.encr_mode = 0x00; /* Encryption not required */ + bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); +} + +static void bt_hci_connection_accept(struct bt_hci_s *hci, + struct bt_device_s *host) +{ + struct bt_hci_link_s *link = qemu_mallocz(sizeof(struct bt_hci_link_s)); + evt_conn_complete params; + uint16_t handle; + uint8_t status = HCI_SUCCESS; + int tries = HCI_HANDLES_MAX; + + /* Make a connection handle */ + do { + while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) + hci->lm.last_handle &= HCI_HANDLES_MAX - 1; + handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; + } while ((handle == hci->asb_handle || handle == hci->psb_handle) && + tries); + + if (!tries) { + qemu_free(link); + bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES); + status = HCI_NO_CONNECTION; + goto complete; + } + + link->btlink.slave = &hci->device; + link->btlink.host = host; + link->handle = handle; + + /* Link established */ + bt_hci_lmp_link_establish(hci, &link->btlink, 0); + +complete: + params.status = status; + params.handle = HNDL(handle); + bacpy(¶ms.bdaddr, &host->bd_addr); + params.link_type = ACL_LINK; + params.encr_mode = 0x00; /* Encryption not required */ + bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); + + /* Neets to be done at the very end because it can trigger a (nested) + * disconnected, in case the other and had cancelled the request + * locally. */ + if (status == HCI_SUCCESS) { + host->reject_reason = 0; + host->lmp_connection_complete(&link->btlink); + } +} + +static void bt_hci_lmp_connection_request(struct bt_link_s *link) +{ + struct bt_hci_s *hci = hci_from_device(link->slave); + evt_conn_request params; + + if (hci->conn_req_host) + return bt_hci_connection_reject(hci, link->host, + HCI_REJECTED_LIMITED_RESOURCES); + hci->conn_req_host = link->host; + /* TODO: if masked and auto-accept, then auto-accept, + * if masked and not auto-accept, then auto-reject */ + /* TODO: kick the hci->conn_accept_timer, timeout after + * hci->conn_accept_tout * 0.625 msec */ + + bacpy(¶ms.bdaddr, &link->host->bd_addr); + memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class)); + params.link_type = ACL_LINK; + bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, EVT_CONN_REQUEST_SIZE); + return; +} + +static void bt_hci_conn_accept_timeout(void *opaque) +{ + struct bt_hci_s *hci = (struct bt_hci_s *) opaque; + + if (!hci->conn_req_host) + /* Already accepted or rejected. If the other end cancelled the + * connection request then we still have to reject or accept it + * and then we'll get a disconnect. */ + return; + + /* TODO */ +} + +/* Remove from the list of devices which we wanted to connect to and + * are awaiting a response from. If the callback sees a response from + * a device which is not on the list it will assume it's a connection + * that's been cancelled by the host in the meantime and immediately + * try to detach the link and send a Connection Complete. */ +static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci, + bdaddr_t *bdaddr) +{ + int i; + + for (i = 0; i < hci->lm.connecting; i ++) + if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) { + if (i < -- hci->lm.connecting) + bacpy(&hci->lm.awaiting_bdaddr[i], + &hci->lm.awaiting_bdaddr[hci->lm.connecting]); + return 0; + } + + return 1; +} + +static void bt_hci_lmp_connection_complete(struct bt_link_s *link) +{ + struct bt_hci_s *hci = hci_from_device(link->host); + evt_conn_complete params; + uint16_t handle; + uint8_t status = HCI_SUCCESS; + int tries = HCI_HANDLES_MAX; + + if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) { + if (!hci->device.reject_reason) + link->slave->lmp_disconnect_slave(link); + handle = 0; + status = HCI_NO_CONNECTION; + goto complete; + } + + if (hci->device.reject_reason) { + handle = 0; + status = hci->device.reject_reason; + goto complete; + } + + /* Make a connection handle */ + do { + while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) + hci->lm.last_handle &= HCI_HANDLES_MAX - 1; + handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; + } while ((handle == hci->asb_handle || handle == hci->psb_handle) && + tries); + + if (!tries) { + link->slave->lmp_disconnect_slave(link); + status = HCI_NO_CONNECTION; + goto complete; + } + + /* Link established */ + link->handle = handle; + bt_hci_lmp_link_establish(hci, link, 1); + +complete: + params.status = status; + params.handle = HNDL(handle); + params.link_type = ACL_LINK; + bacpy(¶ms.bdaddr, &link->slave->bd_addr); + params.encr_mode = 0x00; /* Encryption not required */ + bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); +} + +static void bt_hci_disconnect(struct bt_hci_s *hci, + uint16_t handle, int reason) +{ + struct bt_link_s *btlink = + hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; + struct bt_hci_link_s *link; + evt_disconn_complete params; + + if (bt_hci_role_master(hci, handle)) { + btlink->slave->reject_reason = reason; + btlink->slave->lmp_disconnect_slave(btlink); + /* The link pointer is invalid from now on */ + + goto complete; + } + + btlink->host->reject_reason = reason; + btlink->host->lmp_disconnect_master(btlink); + + /* We are the slave, we get to clean this burden */ + link = (struct bt_hci_link_s *) btlink; + qemu_free(link); + +complete: + bt_hci_lmp_link_teardown(hci, handle); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + params.reason = HCI_CONNECTION_TERMINATED; + bt_hci_event(hci, EVT_DISCONN_COMPLETE, + ¶ms, EVT_DISCONN_COMPLETE_SIZE); +} + +/* TODO: use only one function */ +static void bt_hci_lmp_disconnect_host(struct bt_link_s *link) +{ + struct bt_hci_s *hci = hci_from_device(link->host); + uint16_t handle = link->handle; + evt_disconn_complete params; + + bt_hci_lmp_link_teardown(hci, handle); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + params.reason = hci->device.reject_reason; + bt_hci_event(hci, EVT_DISCONN_COMPLETE, + ¶ms, EVT_DISCONN_COMPLETE_SIZE); +} + +static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink) +{ + struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; + struct bt_hci_s *hci = hci_from_device(btlink->slave); + uint16_t handle = link->handle; + evt_disconn_complete params; + + qemu_free(link); + + bt_hci_lmp_link_teardown(hci, handle); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + params.reason = hci->device.reject_reason; + bt_hci_event(hci, EVT_DISCONN_COMPLETE, + ¶ms, EVT_DISCONN_COMPLETE_SIZE); +} + +static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) +{ + struct bt_device_s *slave; + evt_remote_name_req_complete params; + int len; + + for (slave = hci->device.net->slave; slave; slave = slave->next) + if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) + break; + if (!slave) + return -ENODEV; + + bt_hci_event_status(hci, HCI_SUCCESS); + + params.status = HCI_SUCCESS; + bacpy(¶ms.bdaddr, &slave->bd_addr); + len = snprintf(params.name, sizeof(params.name), + "%s", slave->lmp_name ?: ""); + memset(params.name + len, 0, sizeof(params.name) - len); + bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE, + ¶ms, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE); + + return 0; +} + +static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle) +{ + struct bt_device_s *slave; + evt_read_remote_features_complete params; + + if (bt_hci_handle_bad(hci, handle)) + return -ENODEV; + + slave = bt_hci_remote_dev(hci, handle); + + bt_hci_event_status(hci, HCI_SUCCESS); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + params.features[0] = (slave->lmp_caps >> 0) & 0xff; + params.features[1] = (slave->lmp_caps >> 8) & 0xff; + params.features[2] = (slave->lmp_caps >> 16) & 0xff; + params.features[3] = (slave->lmp_caps >> 24) & 0xff; + params.features[4] = (slave->lmp_caps >> 32) & 0xff; + params.features[5] = (slave->lmp_caps >> 40) & 0xff; + params.features[6] = (slave->lmp_caps >> 48) & 0xff; + params.features[7] = (slave->lmp_caps >> 56) & 0xff; + bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE, + ¶ms, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE); + + return 0; +} + +static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle) +{ + struct bt_device_s *slave; + evt_read_remote_version_complete params; + + if (bt_hci_handle_bad(hci, handle)) + return -ENODEV; + + slave = bt_hci_remote_dev(hci, handle); + + bt_hci_event_status(hci, HCI_SUCCESS); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + params.lmp_ver = 0x03; + params.manufacturer = cpu_to_le16(0xa000); + params.lmp_subver = cpu_to_le16(0xa607); + bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE, + ¶ms, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE); + + return 0; +} + +static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle) +{ + struct bt_device_s *slave; + evt_read_clock_offset_complete params; + + if (bt_hci_handle_bad(hci, handle)) + return -ENODEV; + + slave = bt_hci_remote_dev(hci, handle); + + bt_hci_event_status(hci, HCI_SUCCESS); + + params.status = HCI_SUCCESS; + params.handle = HNDL(handle); + /* TODO: return the clkoff *differenece* */ + params.clock_offset = slave->clkoff; /* Note: no swapping */ + bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE, + ¶ms, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE); + + return 0; +} + +static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link, + uint16_t handle) +{ + evt_mode_change params = { + .status = HCI_SUCCESS, + .handle = HNDL(handle), + .mode = link->acl_mode, + .interval = cpu_to_le16(link->acl_interval), + }; + + bt_hci_event(hci, EVT_MODE_CHANGE, ¶ms, EVT_MODE_CHANGE_SIZE); +} + +static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci, + struct bt_link_s *link, int mode, uint16_t interval) +{ + link->acl_mode = mode; + link->acl_interval = interval; + + bt_hci_event_mode(hci, link, link->handle); + + link->slave->lmp_mode_change(link); +} + +static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink) +{ + struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; + struct bt_hci_s *hci = hci_from_device(btlink->slave); + + bt_hci_event_mode(hci, btlink, link->handle); +} + +static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, + int interval, int mode) +{ + struct bt_hci_master_link_s *link; + + if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) + return -ENODEV; + + link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; + if (link->link->acl_mode != acl_active) { + bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); + return 0; + } + + bt_hci_event_status(hci, HCI_SUCCESS); + + qemu_mod_timer(link->acl_mode_timer, qemu_get_clock(vm_clock) + + muldiv64(interval * 625, ticks_per_sec, 1000000)); + bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); + + return 0; +} + +static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode) +{ + struct bt_hci_master_link_s *link; + + if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) + return -ENODEV; + + link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; + if (link->link->acl_mode != mode) { + bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); + + return 0; + } + + bt_hci_event_status(hci, HCI_SUCCESS); + + qemu_del_timer(link->acl_mode_timer); + bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0); + + return 0; +} + +static void bt_hci_mode_tick(void *opaque) +{ + struct bt_link_s *link = opaque; + struct bt_hci_s *hci = hci_from_device(link->host); + + bt_hci_lmp_mode_change_master(hci, link, acl_active, 0); +} + +void bt_hci_reset(struct bt_hci_s *hci) +{ + hci->acl_len = 0; + hci->last_cmd = 0; + hci->lm.connecting = 0; + + hci->event_mask[0] = 0xff; + hci->event_mask[1] = 0xff; + hci->event_mask[2] = 0xff; + hci->event_mask[3] = 0xff; + hci->event_mask[4] = 0xff; + hci->event_mask[5] = 0x1f; + hci->event_mask[6] = 0x00; + hci->event_mask[7] = 0x00; + hci->device.inquiry_scan = 0; + hci->device.page_scan = 0; + if (hci->device.lmp_name) + free((void *) hci->device.lmp_name); + hci->device.lmp_name = 0; + hci->device.class[0] = 0x00; + hci->device.class[1] = 0x00; + hci->device.class[2] = 0x00; + hci->voice_setting = 0x0000; + hci->conn_accept_tout = 0x1f40; + hci->lm.inquiry_mode = 0x00; + + hci->psb_handle = 0x000; + hci->asb_handle = 0x000; + + /* XXX: qemu_del_timer(sl->acl_mode_timer); for all links */ + qemu_del_timer(hci->lm.inquiry_done); + qemu_del_timer(hci->lm.inquiry_next); + qemu_del_timer(hci->conn_accept_timer); +} + +static void bt_hci_read_local_version_rp(struct bt_hci_s *hci) +{ + read_local_version_rp lv = { + .status = HCI_SUCCESS, + .hci_ver = 0x03, + .hci_rev = cpu_to_le16(0xa607), + .lmp_ver = 0x03, + .manufacturer = cpu_to_le16(0xa000), + .lmp_subver = cpu_to_le16(0xa607), + }; + + bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE); +} + +static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci) +{ + read_local_commands_rp lc = { + .status = HCI_SUCCESS, + .commands = { + /* Keep updated! */ + /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */ + 0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3, + 0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }; + + bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE); +} + +static void bt_hci_read_local_features_rp(struct bt_hci_s *hci) +{ + read_local_features_rp lf = { + .status = HCI_SUCCESS, + .features = { + (hci->device.lmp_caps >> 0) & 0xff, + (hci->device.lmp_caps >> 8) & 0xff, + (hci->device.lmp_caps >> 16) & 0xff, + (hci->device.lmp_caps >> 24) & 0xff, + (hci->device.lmp_caps >> 32) & 0xff, + (hci->device.lmp_caps >> 40) & 0xff, + (hci->device.lmp_caps >> 48) & 0xff, + (hci->device.lmp_caps >> 56) & 0xff, + }, + }; + + bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE); +} + +static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page) +{ + read_local_ext_features_rp lef = { + .status = HCI_SUCCESS, + .page_num = page, + .max_page_num = 0x00, + .features = { + /* Keep updated! */ + 0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80, + }, + }; + if (page) + memset(lef.features, 0, sizeof(lef.features)); + + bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE); +} + +static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci) +{ + read_buffer_size_rp bs = { + /* This can be made configurable, for one standard USB dongle HCI + * the four values are cpu_to_le16(0x0180), 0x40, + * cpu_to_le16(0x0008), cpu_to_le16(0x0008). */ + .status = HCI_SUCCESS, + .acl_mtu = cpu_to_le16(0x0200), + .sco_mtu = 0, + .acl_max_pkt = cpu_to_le16(0x0001), + .sco_max_pkt = cpu_to_le16(0x0000), + }; + + bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE); +} + +/* Deprecated in V2.0 (page 661) */ +static void bt_hci_read_country_code_rp(struct bt_hci_s *hci) +{ + read_country_code_rp cc ={ + .status = HCI_SUCCESS, + .country_code = 0x00, /* North America & Europe^1 and Japan */ + }; + + bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE); + + /* ^1. Except France, sorry */ +} + +static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci) +{ + read_bd_addr_rp ba = { + .status = HCI_SUCCESS, + .bdaddr = BAINIT(&hci->device.bd_addr), + }; + + bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE); +} + +static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle) +{ + read_link_quality_rp lq = { + .status = HCI_SUCCESS, + .handle = HNDL(handle), + .link_quality = 0xff, + }; + + if (bt_hci_handle_bad(hci, handle)) + lq.status = HCI_NO_CONNECTION; + + bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE); + return 0; +} + +/* Generate a Command Complete event with only the Status parameter */ +static inline void bt_hci_event_complete_status(struct bt_hci_s *hci, + uint8_t status) +{ + bt_hci_event_complete(hci, &status, 1); +} + +static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci, + uint8_t status, bdaddr_t *bd_addr) +{ + create_conn_cancel_rp params = { + .status = status, + .bdaddr = BAINIT(bd_addr), + }; + + bt_hci_event_complete(hci, ¶ms, CREATE_CONN_CANCEL_RP_SIZE); +} + +static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci, + uint16_t handle) +{ + evt_auth_complete params = { + .status = HCI_SUCCESS, + .handle = HNDL(handle), + }; + + bt_hci_event(hci, EVT_AUTH_COMPLETE, ¶ms, EVT_AUTH_COMPLETE_SIZE); +} + +static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci, + uint16_t handle, uint8_t mode) +{ + evt_encrypt_change params = { + .status = HCI_SUCCESS, + .handle = HNDL(handle), + .encrypt = mode, + }; + + bt_hci_event(hci, EVT_ENCRYPT_CHANGE, ¶ms, EVT_ENCRYPT_CHANGE_SIZE); +} + +static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci, + bdaddr_t *bd_addr) +{ + remote_name_req_cancel_rp params = { + .status = HCI_INVALID_PARAMETERS, + .bdaddr = BAINIT(bd_addr), + }; + + bt_hci_event_complete(hci, ¶ms, REMOTE_NAME_REQ_CANCEL_RP_SIZE); +} + +static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci, + uint16_t handle) +{ + evt_read_remote_ext_features_complete params = { + .status = HCI_UNSUPPORTED_FEATURE, + .handle = HNDL(handle), + /* Rest uninitialised */ + }; + + bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, + ¶ms, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE); +} + +static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci, + uint16_t handle) +{ + read_lmp_handle_rp params = { + .status = HCI_NO_CONNECTION, + .handle = HNDL(handle), + .reserved = 0, + /* Rest uninitialised */ + }; + + bt_hci_event_complete(hci, ¶ms, READ_LMP_HANDLE_RP_SIZE); +} + +static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci, + int status, uint16_t handle, int master) +{ + role_discovery_rp params = { + .status = status, + .handle = HNDL(handle), + .role = master ? 0x00 : 0x01, + }; + + bt_hci_event_complete(hci, ¶ms, ROLE_DISCOVERY_RP_SIZE); +} + +static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci, + int status, uint16_t handle) +{ + flush_rp params = { + .status = status, + .handle = HNDL(handle), + }; + + bt_hci_event_complete(hci, ¶ms, FLUSH_RP_SIZE); +} + +static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci) +{ + read_local_name_rp params; + params.status = HCI_SUCCESS; + memset(params.name, 0, sizeof(params.name)); + if (hci->device.lmp_name) + strncpy(params.name, hci->device.lmp_name, sizeof(params.name)); + + bt_hci_event_complete(hci, ¶ms, READ_LOCAL_NAME_RP_SIZE); +} + +static inline void bt_hci_event_complete_read_conn_accept_timeout( + struct bt_hci_s *hci) +{ + read_conn_accept_timeout_rp params = { + .status = HCI_SUCCESS, + .timeout = cpu_to_le16(hci->conn_accept_tout), + }; + + bt_hci_event_complete(hci, ¶ms, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE); +} + +static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci) +{ + read_scan_enable_rp params = { + .status = HCI_SUCCESS, + .enable = + (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) | + (hci->device.page_scan ? SCAN_PAGE : 0), + }; + + bt_hci_event_complete(hci, ¶ms, READ_SCAN_ENABLE_RP_SIZE); +} + +static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci) +{ + read_class_of_dev_rp params; + + params.status = HCI_SUCCESS; + memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class)); + + bt_hci_event_complete(hci, ¶ms, READ_CLASS_OF_DEV_RP_SIZE); +} + +static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci) +{ + read_voice_setting_rp params = { + .status = HCI_SUCCESS, + .voice_setting = hci->voice_setting, /* Note: no swapping */ + }; + + bt_hci_event_complete(hci, ¶ms, READ_VOICE_SETTING_RP_SIZE); +} + +static inline void bt_hci_event_complete_read_inquiry_mode( + struct bt_hci_s *hci) +{ + read_inquiry_mode_rp params = { + .status = HCI_SUCCESS, + .mode = hci->lm.inquiry_mode, + }; + + bt_hci_event_complete(hci, ¶ms, READ_INQUIRY_MODE_RP_SIZE); +} + +static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci, + uint16_t handle, int packets) +{ + uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1]; + evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1); + + params->num_hndl = 1; + params->connection->handle = HNDL(handle); + params->connection->num_packets = cpu_to_le16(packets); + + bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1)); +} + +static void bt_submit_hci(struct HCIInfo *info, + const uint8_t *data, int length) +{ + struct bt_hci_s *hci = hci_from_info(info); + uint16_t cmd; + int paramlen, i; + + if (length < HCI_COMMAND_HDR_SIZE) + goto short_hci; + + memcpy(&hci->last_cmd, data, 2); + + cmd = (data[1] << 8) | data[0]; + paramlen = data[2]; + if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0) /* NOP */ + return; + + data += HCI_COMMAND_HDR_SIZE; + length -= HCI_COMMAND_HDR_SIZE; + + if (paramlen > length) + return; + +#define PARAM(cmd, param) (((cmd##_cp *) data)->param) +#define PARAM16(cmd, param) le16_to_cpup(&PARAM(cmd, param)) +#define PARAMHANDLE(cmd) HNDL(PARAM(cmd, handle)) +#define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci + /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp + * needs to be updated every time a command is implemented here! */ + switch (cmd) { + case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY): + LENGTH_CHECK(inquiry); + + if (PARAM(inquiry, length) < 1) { + bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + hci->lm.inquire = 1; + hci->lm.periodic = 0; + hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX; + hci->lm.responses = 0; + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_inquiry_start(hci, PARAM(inquiry, length)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL): + if (!hci->lm.inquire || hci->lm.periodic) { + fprintf(stderr, "%s: Inquiry Cancel should only be issued after " + "the Inquiry command has been issued, a Command " + "Status event has been received for the Inquiry " + "command, and before the Inquiry Complete event " + "occurs", __FUNCTION__); + bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); + break; + } + + hci->lm.inquire = 0; + qemu_del_timer(hci->lm.inquiry_done); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY): + LENGTH_CHECK(periodic_inquiry); + + if (!(PARAM(periodic_inquiry, length) < + PARAM16(periodic_inquiry, min_period) && + PARAM16(periodic_inquiry, min_period) < + PARAM16(periodic_inquiry, max_period)) || + PARAM(periodic_inquiry, length) < 1 || + PARAM16(periodic_inquiry, min_period) < 2 || + PARAM16(periodic_inquiry, max_period) < 3) { + bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + hci->lm.inquire = 1; + hci->lm.periodic = 1; + hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp); + hci->lm.responses = 0; + hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY): + if (!hci->lm.inquire || !hci->lm.periodic) { + fprintf(stderr, "%s: Inquiry Cancel should only be issued after " + "the Inquiry command has been issued, a Command " + "Status event has been received for the Inquiry " + "command, and before the Inquiry Complete event " + "occurs", __FUNCTION__); + bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); + break; + } + hci->lm.inquire = 0; + qemu_del_timer(hci->lm.inquiry_done); + qemu_del_timer(hci->lm.inquiry_next); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN): + LENGTH_CHECK(create_conn); + + if (hci->lm.connecting >= HCI_HANDLES_MAX) { + bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES); + break; + } + bt_hci_event_status(hci, HCI_SUCCESS); + + if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr))) + bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT): + LENGTH_CHECK(disconnect); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) { + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + } + + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_disconnect(hci, PARAMHANDLE(disconnect), + PARAM(disconnect, reason)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL): + LENGTH_CHECK(create_conn_cancel); + + if (bt_hci_lmp_connection_ready(hci, + &PARAM(create_conn_cancel, bdaddr))) { + for (i = 0; i < HCI_HANDLES_MAX; i ++) + if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link && + !bacmp(&hci->lm.handle[i].link->slave->bd_addr, + &PARAM(create_conn_cancel, bdaddr))) + break; + + bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ? + HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION, + &PARAM(create_conn_cancel, bdaddr)); + } else + bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS, + &PARAM(create_conn_cancel, bdaddr)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ): + LENGTH_CHECK(accept_conn_req); + + if (!hci->conn_req_host || + bacmp(&PARAM(accept_conn_req, bdaddr), + &hci->conn_req_host->bd_addr)) { + bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_connection_accept(hci, hci->conn_req_host); + hci->conn_req_host = 0; + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ): + LENGTH_CHECK(reject_conn_req); + + if (!hci->conn_req_host || + bacmp(&PARAM(reject_conn_req, bdaddr), + &hci->conn_req_host->bd_addr)) { + bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_connection_reject(hci, hci->conn_req_host, + PARAM(reject_conn_req, reason)); + bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr); + hci->conn_req_host = 0; + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED): + LENGTH_CHECK(auth_requested); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + else { + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested)); + } + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT): + LENGTH_CHECK(set_conn_encrypt); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + else { + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_event_encrypt_change(hci, + PARAMHANDLE(set_conn_encrypt), + PARAM(set_conn_encrypt, encrypt)); + } + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ): + LENGTH_CHECK(remote_name_req); + + if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL): + LENGTH_CHECK(remote_name_req_cancel); + + bt_hci_event_complete_name_cancel(hci, + &PARAM(remote_name_req_cancel, bdaddr)); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES): + LENGTH_CHECK(read_remote_features); + + if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES): + LENGTH_CHECK(read_remote_ext_features); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + else { + bt_hci_event_status(hci, HCI_SUCCESS); + bt_hci_event_read_remote_ext_features(hci, + PARAMHANDLE(read_remote_ext_features)); + } + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION): + LENGTH_CHECK(read_remote_version); + + if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET): + LENGTH_CHECK(read_clock_offset); + + if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset))) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE): + LENGTH_CHECK(read_lmp_handle); + + /* TODO: */ + bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle)); + break; + + case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE): + LENGTH_CHECK(hold_mode); + + if (PARAM16(hold_mode, min_interval) > + PARAM16(hold_mode, max_interval) || + PARAM16(hold_mode, min_interval) < 0x0002 || + PARAM16(hold_mode, max_interval) > 0xff00 || + (PARAM16(hold_mode, min_interval) & 1) || + (PARAM16(hold_mode, max_interval) & 1)) { + bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode), + PARAM16(hold_mode, max_interval), + acl_hold)) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE): + LENGTH_CHECK(park_mode); + + if (PARAM16(park_mode, min_interval) > + PARAM16(park_mode, max_interval) || + PARAM16(park_mode, min_interval) < 0x000e || + (PARAM16(park_mode, min_interval) & 1) || + (PARAM16(park_mode, max_interval) & 1)) { + bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode), + PARAM16(park_mode, max_interval), + acl_parked)) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE): + LENGTH_CHECK(exit_park_mode); + + if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode), + acl_parked)) + bt_hci_event_status(hci, HCI_NO_CONNECTION); + break; + + case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY): + LENGTH_CHECK(role_discovery); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery))) + bt_hci_event_complete_role_discovery(hci, + HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0); + else + bt_hci_event_complete_role_discovery(hci, + HCI_SUCCESS, PARAMHANDLE(role_discovery), + bt_hci_role_master(hci, + PARAMHANDLE(role_discovery))); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK): + LENGTH_CHECK(set_event_mask); + + memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET): + bt_hci_reset(hci); + bt_hci_event_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT): + if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL) + /* No length check */; + else + LENGTH_CHECK(set_event_flt); + + /* Filters are not implemented */ + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH): + LENGTH_CHECK(flush); + + if (bt_hci_handle_bad(hci, PARAMHANDLE(flush))) + bt_hci_event_complete_flush(hci, + HCI_NO_CONNECTION, PARAMHANDLE(flush)); + else { + /* TODO: ordering? */ + bt_hci_event(hci, EVT_FLUSH_OCCURRED, + &PARAM(flush, handle), + EVT_FLUSH_OCCURRED_SIZE); + bt_hci_event_complete_flush(hci, + HCI_SUCCESS, PARAMHANDLE(flush)); + } + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): + LENGTH_CHECK(change_local_name); + + if (hci->device.lmp_name) + free((void *) hci->device.lmp_name); + hci->device.lmp_name = strndup(PARAM(change_local_name, name), + sizeof(PARAM(change_local_name, name))); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME): + bt_hci_event_complete_read_local_name(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT): + bt_hci_event_complete_read_conn_accept_timeout(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT): + /* TODO */ + LENGTH_CHECK(write_conn_accept_timeout); + + if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 || + PARAM16(write_conn_accept_timeout, timeout) > 0xb540) { + bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE): + bt_hci_event_complete_read_scan_enable(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE): + LENGTH_CHECK(write_scan_enable); + + /* TODO: check that the remaining bits are all 0 */ + hci->device.inquiry_scan = + !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY); + hci->device.page_scan = + !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV): + bt_hci_event_complete_read_local_class(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV): + LENGTH_CHECK(write_class_of_dev); + + memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class), + sizeof(PARAM(write_class_of_dev, dev_class))); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING): + bt_hci_event_complete_voice_setting(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING): + LENGTH_CHECK(write_voice_setting); + + hci->voice_setting = PARAM(write_voice_setting, voice_setting); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS): + if (length < data[0] * 2 + 1) + goto short_hci; + + for (i = 0; i < data[0]; i ++) + if (bt_hci_handle_bad(hci, + data[i * 2 + 1] | (data[i * 2 + 2] << 8))) + bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE): + /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40) + * else + * goto unknown_command */ + bt_hci_event_complete_read_inquiry_mode(hci); + break; + + case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE): + /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80) + * else + * goto unknown_command */ + LENGTH_CHECK(write_inquiry_mode); + + if (PARAM(write_inquiry_mode, mode) > 0x01) { + bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); + break; + } + + hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode); + bt_hci_event_complete_status(hci, HCI_SUCCESS); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION): + bt_hci_read_local_version_rp(hci); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS): + bt_hci_read_local_commands_rp(hci); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES): + bt_hci_read_local_features_rp(hci); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES): + LENGTH_CHECK(read_local_ext_features); + + bt_hci_read_local_ext_features_rp(hci, + PARAM(read_local_ext_features, page_num)); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE): + bt_hci_read_buffer_size_rp(hci); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE): + bt_hci_read_country_code_rp(hci); + break; + + case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR): + bt_hci_read_bd_addr_rp(hci); + break; + + case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY): + LENGTH_CHECK(read_link_quality); + + bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality)); + break; + + default: + bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND); + break; + + short_hci: + fprintf(stderr, "%s: HCI packet too short (%iB)\n", + __FUNCTION__, length); + bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); + break; + } +} + +/* We could perform fragmentation here, we can't do "recombination" because + * at this layer the length of the payload is not know ahead, so we only + * know that a packet contained the last fragment of the SDU when the next + * SDU starts. */ +static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle, + const uint8_t *data, int start, int len) +{ + struct hci_acl_hdr *pkt = (void *) hci->acl_buf; + + /* TODO: packet flags */ + /* TODO: avoid memcpy'ing */ + + if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) { + fprintf(stderr, "%s: can't take ACL packets %i bytes long\n", + __FUNCTION__, len); + return; + } + memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len); + + pkt->handle = cpu_to_le16( + acl_handle_pack(handle, start ? ACL_START : ACL_CONT)); + pkt->dlen = cpu_to_le16(len); + hci->info.acl_recv(hci->info.opaque, + hci->acl_buf, len + HCI_ACL_HDR_SIZE); +} + +static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink, + const uint8_t *data, int start, int len) +{ + struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; + + bt_hci_lmp_acl_data(hci_from_device(btlink->slave), + link->handle, data, start, len); +} + +static void bt_hci_lmp_acl_data_host(struct bt_link_s *link, + const uint8_t *data, int start, int len) +{ + bt_hci_lmp_acl_data(hci_from_device(link->host), + link->handle, data, start, len); +} + +static void bt_submit_acl(struct HCIInfo *info, + const uint8_t *data, int length) +{ + struct bt_hci_s *hci = hci_from_info(info); + uint16_t handle; + int datalen, flags; + struct bt_link_s *link; + + if (length < HCI_ACL_HDR_SIZE) { + fprintf(stderr, "%s: ACL packet too short (%iB)\n", + __FUNCTION__, length); + return; + } + + handle = acl_handle((data[1] << 8) | data[0]); + flags = acl_flags((data[1] << 8) | data[0]); + datalen = (data[3] << 8) | data[2]; + data += HCI_ACL_HDR_SIZE; + length -= HCI_ACL_HDR_SIZE; + + if (bt_hci_handle_bad(hci, handle)) { + fprintf(stderr, "%s: invalid ACL handle %03x\n", + __FUNCTION__, handle); + /* TODO: signal an error */ + return; + } + handle &= ~HCI_HANDLE_OFFSET; + + if (datalen > length) { + fprintf(stderr, "%s: ACL packet too short (%iB < %iB)\n", + __FUNCTION__, length, datalen); + return; + } + + link = hci->lm.handle[handle].link; + + if ((flags & ~3) == ACL_ACTIVE_BCAST) { + if (!hci->asb_handle) + hci->asb_handle = handle; + else if (handle != hci->asb_handle) { + fprintf(stderr, "%s: Bad handle %03x in Active Slave Broadcast\n", + __FUNCTION__, handle); + /* TODO: signal an error */ + return; + } + + /* TODO */ + } + + if ((flags & ~3) == ACL_PICO_BCAST) { + if (!hci->psb_handle) + hci->psb_handle = handle; + else if (handle != hci->psb_handle) { + fprintf(stderr, "%s: Bad handle %03x in Parked Slave Broadcast\n", + __FUNCTION__, handle); + /* TODO: signal an error */ + return; + } + + /* TODO */ + } + + /* TODO: increase counter and send EVT_NUM_COMP_PKTS */ + bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1); + + /* Do this last as it can trigger further events even in this HCI */ + hci->lm.handle[handle].lmp_acl_data(link, data, + (flags & 3) == ACL_START, length); +} + +static void bt_submit_sco(struct HCIInfo *info, + const uint8_t *data, int length) +{ + struct bt_hci_s *hci = hci_from_info(info); + struct bt_link_s *link; + uint16_t handle; + int datalen; + + if (length < 3) + return; + + handle = acl_handle((data[1] << 8) | data[0]); + datalen = data[2]; + data += 3; + length -= 3; + + if (bt_hci_handle_bad(hci, handle)) { + fprintf(stderr, "%s: invalid SCO handle %03x\n", + __FUNCTION__, handle); + return; + } + handle &= ~HCI_HANDLE_OFFSET; + + if (datalen > length) { + fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n", + __FUNCTION__, length, datalen); + return; + } + + link = hci->lm.handle[handle].link; + /* TODO */ + + /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous + * Flow Control is enabled. + * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and + * page 514.) */ +} + +static uint8_t *bt_hci_evt_packet(void *opaque) +{ + /* TODO: allocate a packet from upper layer */ + struct bt_hci_s *s = opaque; + + return s->evt_buf; +} + +static void bt_hci_evt_submit(void *opaque, int len) +{ + /* TODO: notify upper layer */ + struct bt_hci_s *s = opaque; + + return s->info.evt_recv(s->info.opaque, s->evt_buf, len); +} + +static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr) +{ + struct bt_hci_s *hci = hci_from_info(info); + + bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr); + return 0; +} + +static void bt_hci_done(struct HCIInfo *info); +static void bt_hci_destroy(struct bt_device_s *dev) +{ + struct bt_hci_s *hci = hci_from_device(dev); + + return bt_hci_done(&hci->info); +} + +struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) +{ + struct bt_hci_s *s = qemu_mallocz(sizeof(struct bt_hci_s)); + + s->lm.inquiry_done = qemu_new_timer(vm_clock, bt_hci_inquiry_done, s); + s->lm.inquiry_next = qemu_new_timer(vm_clock, bt_hci_inquiry_next, s); + s->conn_accept_timer = + qemu_new_timer(vm_clock, bt_hci_conn_accept_timeout, s); + + s->evt_packet = bt_hci_evt_packet; + s->evt_submit = bt_hci_evt_submit; + s->opaque = s; + + bt_device_init(&s->device, net); + s->device.lmp_connection_request = bt_hci_lmp_connection_request; + s->device.lmp_connection_complete = bt_hci_lmp_connection_complete; + s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host; + s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave; + s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave; + s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host; + s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave; + + /* Keep updated! */ + /* Also keep in sync with supported commands bitmask in + * bt_hci_read_local_commands_rp */ + s->device.lmp_caps = 0x8000199b7e85355fll; + + bt_hci_reset(s); + + s->info.cmd_send = bt_submit_hci; + s->info.sco_send = bt_submit_sco; + s->info.acl_send = bt_submit_acl; + s->info.bdaddr_set = bt_hci_bdaddr_set; + + s->device.handle_destroy = bt_hci_destroy; + + return &s->info; +} + +static void bt_hci_done(struct HCIInfo *info) +{ + struct bt_hci_s *hci = hci_from_info(info); + int handle; + + bt_device_done(&hci->device); + + if (hci->device.lmp_name) + free((void *) hci->device.lmp_name); + + /* Be gentle and send DISCONNECT to all connected peers and those + * currently waiting for us to accept or reject a connection request. + * This frees the links. */ + if (hci->conn_req_host) + return bt_hci_connection_reject(hci, + hci->conn_req_host, HCI_OE_POWER_OFF); + + for (handle = HCI_HANDLE_OFFSET; + handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++) + if (!bt_hci_handle_bad(hci, handle)) + bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF); + + /* TODO: this is not enough actually, there may be slaves from whom + * we have requested a connection who will soon (or not) respond with + * an accept or a reject, so we should also check if hci->lm.connecting + * is non-zero and if so, avoid freeing the hci but otherwise disappear + * from all qemu social life (e.g. stop scanning and request to be + * removed from s->device.net) and arrange for + * s->device.lmp_connection_complete to free the remaining bits once + * hci->lm.awaiting_bdaddr[] is empty. */ + + qemu_free_timer(hci->lm.inquiry_done); + qemu_free_timer(hci->lm.inquiry_next); + qemu_free_timer(hci->conn_accept_timer); + + qemu_free(hci); +} diff -Nur kvm-76/qemu/hw/bt-hid.c kvm-userspace/qemu/hw/bt-hid.c --- kvm-76/qemu/hw/bt-hid.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt-hid.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,571 @@ +/* + * QEMU Bluetooth HID Profile wrapper for USB HID. + * + * Copyright (C) 2007-2008 OpenMoko, Inc. + * Written by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "usb.h" +#include "bt.h" + +enum hid_transaction_req { + BT_HANDSHAKE = 0x0, + BT_HID_CONTROL = 0x1, + BT_GET_REPORT = 0x4, + BT_SET_REPORT = 0x5, + BT_GET_PROTOCOL = 0x6, + BT_SET_PROTOCOL = 0x7, + BT_GET_IDLE = 0x8, + BT_SET_IDLE = 0x9, + BT_DATA = 0xa, + BT_DATC = 0xb, +}; + +enum hid_transaction_handshake { + BT_HS_SUCCESSFUL = 0x0, + BT_HS_NOT_READY = 0x1, + BT_HS_ERR_INVALID_REPORT_ID = 0x2, + BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3, + BT_HS_ERR_INVALID_PARAMETER = 0x4, + BT_HS_ERR_UNKNOWN = 0xe, + BT_HS_ERR_FATAL = 0xf, +}; + +enum hid_transaction_control { + BT_HC_NOP = 0x0, + BT_HC_HARD_RESET = 0x1, + BT_HC_SOFT_RESET = 0x2, + BT_HC_SUSPEND = 0x3, + BT_HC_EXIT_SUSPEND = 0x4, + BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5, +}; + +enum hid_protocol { + BT_HID_PROTO_BOOT = 0, + BT_HID_PROTO_REPORT = 1, +}; + +enum hid_boot_reportid { + BT_HID_BOOT_INVALID = 0, + BT_HID_BOOT_KEYBOARD, + BT_HID_BOOT_MOUSE, +}; + +enum hid_data_pkt { + BT_DATA_OTHER = 0, + BT_DATA_INPUT, + BT_DATA_OUTPUT, + BT_DATA_FEATURE, +}; + +#define BT_HID_MTU 48 + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_REPORT 0x2109 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +struct bt_hid_device_s { + struct bt_l2cap_device_s btdev; + struct bt_l2cap_conn_params_s *control; + struct bt_l2cap_conn_params_s *interrupt; + USBDevice *usbdev; + + int proto; + int connected; + int data_type; + int intr_state; + struct { + int len; + uint8_t buffer[1024]; + } dataother, datain, dataout, feature, intrdataout; + enum { + bt_state_ready, + bt_state_transaction, + bt_state_suspend, + } state; +}; + +static void bt_hid_reset(struct bt_hid_device_s *s) +{ + struct bt_scatternet_s *net = s->btdev.device.net; + + /* Go as far as... */ + bt_l2cap_device_done(&s->btdev); + bt_l2cap_device_init(&s->btdev, net); + + s->usbdev->handle_reset(s->usbdev); + s->proto = BT_HID_PROTO_REPORT; + s->state = bt_state_ready; + s->dataother.len = 0; + s->datain.len = 0; + s->dataout.len = 0; + s->feature.len = 0; + s->intrdataout.len = 0; + s->intr_state = 0; +} + +static int bt_hid_out(struct bt_hid_device_s *s) +{ + USBPacket p; + + if (s->data_type == BT_DATA_OUTPUT) { + p.pid = USB_TOKEN_OUT; + p.devep = 1; + p.data = s->dataout.buffer; + p.len = s->dataout.len; + s->dataout.len = s->usbdev->handle_data(s->usbdev, &p); + + return s->dataout.len; + } + + if (s->data_type == BT_DATA_FEATURE) { + /* XXX: + * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE + * or a SET_REPORT? */ + p.devep = 0; + } + + return -1; +} + +static int bt_hid_in(struct bt_hid_device_s *s) +{ + USBPacket p; + + p.pid = USB_TOKEN_IN; + p.devep = 1; + p.data = s->datain.buffer; + p.len = sizeof(s->datain.buffer); + s->datain.len = s->usbdev->handle_data(s->usbdev, &p); + + return s->datain.len; +} + +static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) +{ + *s->control->sdu_out(s->control, 1) = + (BT_HANDSHAKE << 4) | result; + s->control->sdu_submit(s->control); +} + +static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) +{ + *s->control->sdu_out(s->control, 1) = + (BT_HID_CONTROL << 4) | operation; + s->control->sdu_submit(s->control); +} + +static void bt_hid_disconnect(struct bt_hid_device_s *s) +{ + /* Disconnect s->control and s->interrupt */ +} + +static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, + const uint8_t *data, int len) +{ + uint8_t *pkt, hdr = (BT_DATA << 4) | type; + int plen; + + do { + plen = MIN(len, ch->remote_mtu - 1); + pkt = ch->sdu_out(ch, plen + 1); + + pkt[0] = hdr; + if (plen) + memcpy(pkt + 1, data, plen); + ch->sdu_submit(ch); + + len -= plen; + data += plen; + hdr = (BT_DATC << 4) | type; + } while (plen == ch->remote_mtu - 1); +} + +static void bt_hid_control_transaction(struct bt_hid_device_s *s, + const uint8_t *data, int len) +{ + uint8_t type, parameter; + int rlen, ret = -1; + if (len < 1) + return; + + type = data[0] >> 4; + parameter = data[0] & 0xf; + + switch (type) { + case BT_HANDSHAKE: + case BT_DATA: + switch (parameter) { + default: + /* These are not expected to be sent this direction. */ + ret = BT_HS_ERR_INVALID_PARAMETER; + } + break; + + case BT_HID_CONTROL: + if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && + s->state == bt_state_transaction)) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + switch (parameter) { + case BT_HC_NOP: + break; + case BT_HC_HARD_RESET: + case BT_HC_SOFT_RESET: + bt_hid_reset(s); + break; + case BT_HC_SUSPEND: + if (s->state == bt_state_ready) + s->state = bt_state_suspend; + else + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + case BT_HC_EXIT_SUSPEND: + if (s->state == bt_state_suspend) + s->state = bt_state_ready; + else + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + case BT_HC_VIRTUAL_CABLE_UNPLUG: + bt_hid_disconnect(s); + break; + default: + ret = BT_HS_ERR_INVALID_PARAMETER; + } + break; + + case BT_GET_REPORT: + /* No ReportIDs declared. */ + if (((parameter & 8) && len != 3) || + (!(parameter & 8) && len != 1) || + s->state != bt_state_ready) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + if (parameter & 8) + rlen = data[2] | (data[3] << 8); + else + rlen = INT_MAX; + switch (parameter & 3) { + case BT_DATA_OTHER: + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + case BT_DATA_INPUT: + /* Here we can as well poll s->usbdev */ + bt_hid_send_data(s->control, BT_DATA_INPUT, + s->datain.buffer, MIN(rlen, s->datain.len)); + break; + case BT_DATA_OUTPUT: + bt_hid_send_data(s->control, BT_DATA_OUTPUT, + s->dataout.buffer, MIN(rlen, s->dataout.len)); + break; + case BT_DATA_FEATURE: + bt_hid_send_data(s->control, BT_DATA_FEATURE, + s->feature.buffer, MIN(rlen, s->feature.len)); + break; + } + break; + + case BT_SET_REPORT: + if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || + (parameter & 3) == BT_DATA_OTHER || + (parameter & 3) == BT_DATA_INPUT) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + s->data_type = parameter & 3; + if (s->data_type == BT_DATA_OUTPUT) { + s->dataout.len = len - 1; + memcpy(s->dataout.buffer, data + 1, s->dataout.len); + } else { + s->feature.len = len - 1; + memcpy(s->feature.buffer, data + 1, s->feature.len); + } + if (len == BT_HID_MTU) + s->state = bt_state_transaction; + else + bt_hid_out(s); + break; + + case BT_GET_PROTOCOL: + if (len != 1 || s->state == bt_state_transaction) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + *s->control->sdu_out(s->control, 1) = s->proto; + s->control->sdu_submit(s->control); + break; + + case BT_SET_PROTOCOL: + if (len != 1 || s->state == bt_state_transaction || + (parameter != BT_HID_PROTO_BOOT && + parameter != BT_HID_PROTO_REPORT)) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + s->proto = parameter; + s->usbdev->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, 0); + ret = BT_HS_SUCCESSFUL; + break; + + case BT_GET_IDLE: + if (len != 1 || s->state == bt_state_transaction) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + s->usbdev->handle_control(s->usbdev, GET_IDLE, 0, 0, 1, + s->control->sdu_out(s->control, 1)); + s->control->sdu_submit(s->control); + break; + + case BT_SET_IDLE: + if (len != 2 || s->state == bt_state_transaction) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + + /* We don't need to know about the Idle Rate here really, + * so just pass it on to the device. */ + ret = s->usbdev->handle_control(s->usbdev, + SET_IDLE, data[1], 0, 0, 0) ? + BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; + /* XXX: Does this generate a handshake? */ + break; + + case BT_DATC: + if (len > BT_HID_MTU || s->state != bt_state_transaction) { + ret = BT_HS_ERR_INVALID_PARAMETER; + break; + } + if (s->data_type == BT_DATA_OUTPUT) { + memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); + s->dataout.len += len - 1; + } else { + memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); + s->feature.len += len - 1; + } + if (len < BT_HID_MTU) { + bt_hid_out(s); + s->state = bt_state_ready; + } + break; + + default: + ret = BT_HS_ERR_UNSUPPORTED_REQUEST; + } + + if (ret != -1) + bt_hid_send_handshake(s, ret); +} + +static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) +{ + struct bt_hid_device_s *hid = opaque; + + return bt_hid_control_transaction(hid, data, len); +} + +static void bt_hid_datain(void *opaque) +{ + struct bt_hid_device_s *hid = opaque; + + /* If suspended, wake-up and send a wake-up event first. We might + * want to also inspect the input report and ignore event like + * mouse movements until a button event occurs. */ + if (hid->state == bt_state_suspend) { + hid->state = bt_state_ready; + } + + if (bt_hid_in(hid) > 0) + /* TODO: when in boot-mode precede any Input reports with the ReportID + * byte, here and in GetReport/SetReport on the Control channel. */ + bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, + hid->datain.buffer, hid->datain.len); +} + +static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) +{ + struct bt_hid_device_s *hid = opaque; + + if (len > BT_HID_MTU || len < 1) + goto bad; + if ((data[0] & 3) != BT_DATA_OUTPUT) + goto bad; + if ((data[0] >> 4) == BT_DATA) { + if (hid->intr_state) + goto bad; + + hid->data_type = BT_DATA_OUTPUT; + hid->intrdataout.len = 0; + } else if ((data[0] >> 4) == BT_DATC) { + if (!hid->intr_state) + goto bad; + } else + goto bad; + + memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); + hid->intrdataout.len += len - 1; + hid->intr_state = (len == BT_HID_MTU); + if (!hid->intr_state) { + memcpy(hid->dataout.buffer, hid->intrdataout.buffer, + hid->dataout.len = hid->intrdataout.len); + bt_hid_out(hid); + } + + return; +bad: + fprintf(stderr, "%s: bad transaction on Interrupt channel.\n", + __FUNCTION__); +} + +/* "Virtual cable" plug/unplug event. */ +static void bt_hid_connected_update(struct bt_hid_device_s *hid) +{ + int prev = hid->connected; + + hid->connected = hid->control && hid->interrupt; + + /* Stop page-/inquiry-scanning when a host is connected. */ + hid->btdev.device.page_scan = !hid->connected; + hid->btdev.device.inquiry_scan = !hid->connected; + + if (hid->connected && !prev) { + hid->usbdev->handle_reset(hid->usbdev); + hid->proto = BT_HID_PROTO_REPORT; + } + + /* Should set HIDVirtualCable in SDP (possibly need to check that SDP + * isn't destroyed yet, in case we're being called from handle_destroy) */ +} + +static void bt_hid_close_control(void *opaque) +{ + struct bt_hid_device_s *hid = opaque; + + hid->control = 0; + bt_hid_connected_update(hid); +} + +static void bt_hid_close_interrupt(void *opaque) +{ + struct bt_hid_device_s *hid = opaque; + + hid->interrupt = 0; + bt_hid_connected_update(hid); +} + +static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params) +{ + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; + + if (hid->control) + return 1; + + hid->control = params; + hid->control->opaque = hid; + hid->control->close = bt_hid_close_control; + hid->control->sdu_in = bt_hid_control_sdu; + + bt_hid_connected_update(hid); + + return 0; +} + +static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params) +{ + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; + + if (hid->interrupt) + return 1; + + hid->interrupt = params; + hid->interrupt->opaque = hid; + hid->interrupt->close = bt_hid_close_interrupt; + hid->interrupt->sdu_in = bt_hid_interrupt_sdu; + + bt_hid_connected_update(hid); + + return 0; +} + +static void bt_hid_destroy(struct bt_device_s *dev) +{ + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; + + if (hid->connected) + bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); + bt_l2cap_device_done(&hid->btdev); + + hid->usbdev->handle_destroy(hid->usbdev); + + qemu_free(hid); +} + +enum peripheral_minor_class { + class_other = 0 << 4, + class_keyboard = 1 << 4, + class_pointing = 2 << 4, + class_combo = 3 << 4, +}; + +static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, + USBDevice *dev, enum peripheral_minor_class minor) +{ + struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); + uint32_t class = + /* Format type */ + (0 << 0) | + /* Device class */ + (minor << 2) | + (5 << 8) | /* "Peripheral" */ + /* Service classes */ + (1 << 13) | /* Limited discoverable mode */ + (1 << 19); /* Capturing device (?) */ + + bt_l2cap_device_init(&s->btdev, net); + bt_l2cap_sdp_init(&s->btdev); + bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, + BT_HID_MTU, bt_hid_new_control_ch); + bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, + BT_HID_MTU, bt_hid_new_interrupt_ch); + + s->usbdev = dev; + s->btdev.device.lmp_name = s->usbdev->devname; + usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); + + s->btdev.device.handle_destroy = bt_hid_destroy; + + s->btdev.device.class[0] = (class >> 0) & 0xff; + s->btdev.device.class[1] = (class >> 8) & 0xff; + s->btdev.device.class[2] = (class >> 16) & 0xff; + + return &s->btdev.device; +} + +struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) +{ + return bt_hid_init(net, usb_keyboard_init(), class_keyboard); +} diff -Nur kvm-76/qemu/hw/bt-l2cap.c kvm-userspace/qemu/hw/bt-l2cap.c --- kvm-76/qemu/hw/bt-l2cap.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt-l2cap.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,1364 @@ +/* + * QEMU Bluetooth L2CAP logic. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "qemu-common.h" +#include "qemu-timer.h" +#include "bt.h" + +#define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ + +struct l2cap_instance_s { + struct bt_link_s *link; + struct bt_l2cap_device_s *dev; + int role; + + uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); + int frame_in_len; + + uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); + int frame_out_len; + + /* Signalling channel timers. They exist per-request but we can make + * sure we have no more than one outstanding request at any time. */ + QEMUTimer *rtx; + QEMUTimer *ertx; + + int last_id; + int next_id; + + struct l2cap_chan_s { + struct bt_l2cap_conn_params_s params; + + void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid, + const l2cap_hdr *hdr, int len); + int mps; + int min_mtu; + + struct l2cap_instance_s *l2cap; + + /* Only allocated channels */ + uint16_t remote_cid; +#define L2CAP_CFG_INIT 2 +#define L2CAP_CFG_ACC 1 + int config_req_id; /* TODO: handle outgoing requests generically */ + int config; + + /* Only connection-oriented channels. Note: if we allow the tx and + * rx traffic to be in different modes at any time, we need two. */ + int mode; + + /* Only flow-controlled, connection-oriented channels */ + uint8_t sdu[65536]; /* TODO: dynamically allocate */ + int len_cur, len_total; + int rexmit; + int monitor_timeout; + QEMUTimer *monitor_timer; + QEMUTimer *retransmission_timer; + } *cid[L2CAP_CID_MAX]; + /* The channel state machine states map as following: + * CLOSED -> !cid[N] + * WAIT_CONNECT -> never occurs + * WAIT_CONNECT_RSP -> never occurs + * CONFIG -> cid[N] && config < 3 + * WAIT_CONFIG -> never occurs, cid[N] && config == 0 && !config_r + * WAIT_SEND_CONFIG -> never occurs, cid[N] && config == 1 && !config_r + * WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id + * WAIT_CONFIG_RSP -> cid[N] && config == 1 && config_req_id + * WAIT_CONFIG_REQ -> cid[N] && config == 2 + * OPEN -> cid[N] && config == 3 + * WAIT_DISCONNECT -> never occurs + */ + + struct l2cap_chan_s signalling_ch; + struct l2cap_chan_s group_ch; +}; + +struct slave_l2cap_instance_s { + struct bt_link_s link; /* Underlying logical link (ACL) */ + struct l2cap_instance_s l2cap; +}; + +struct bt_l2cap_psm_s { + int psm; + int min_mtu; + int (*new_channel)(struct bt_l2cap_device_s *device, + struct bt_l2cap_conn_params_s *params); + struct bt_l2cap_psm_s *next; +}; + +static const uint16_t l2cap_fcs16_table[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, +}; + +static uint16_t l2cap_fcs16(const uint8_t *message, int len) +{ + uint16_t fcs = 0x0000; + + while (len --) +#if 0 + { + int i; + + fcs ^= *message ++; + for (i = 8; i; -- i) + if (fcs & 1) + fcs = (fcs >> 1) ^ 0xa001; + else + fcs = (fcs >> 1); + } +#else + fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff]; +#endif + + return fcs; +} + +/* L2CAP layer logic (protocol) */ + +static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch) +{ +#if 0 + if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit) + qemu_mod_timer(ch->retransmission_timer); + else + qemu_del_timer(ch->retransmission_timer); +#endif +} + +static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch) +{ +#if 0 + if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit) + qemu_mod_timer(ch->monitor_timer); + else + qemu_del_timer(ch->monitor_timer); +#endif +} + +static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id, + uint16_t reason, const void *data, int plen) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_cmd_rej *params; + uint16_t len; + + reason = cpu_to_le16(reason); + len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen); + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_COMMAND_REJ; + hdr->ident = id; + memcpy(&hdr->len, &len, sizeof(hdr->len)); + memcpy(¶ms->reason, &reason, sizeof(reason)); + if (plen) + memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id, + uint16_t reason, uint16_t dcid, uint16_t scid) +{ + l2cap_cmd_rej_cid params = { + .dcid = dcid, + .scid = scid, + }; + + l2cap_command_reject(l2cap, id, reason, ¶ms, L2CAP_CMD_REJ_CID_SIZE); +} + +static void l2cap_connection_response(struct l2cap_instance_s *l2cap, + int dcid, int scid, int result, int status) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_conn_rsp *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_CONN_RSP; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE); + + params->dcid = cpu_to_le16(dcid); + params->scid = cpu_to_le16(scid); + params->result = cpu_to_le16(result); + params->status = cpu_to_le16(status); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_configuration_request(struct l2cap_instance_s *l2cap, + int dcid, int flag, const uint8_t *data, int len) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_conf_req *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len)); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + /* TODO: unify the id sequencing */ + l2cap->last_id = l2cap->next_id; + l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; + + hdr->code = L2CAP_CONF_REQ; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len)); + + params->dcid = cpu_to_le16(dcid); + params->flags = cpu_to_le16(flag); + if (len) + memcpy(params->data, data, len); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_configuration_response(struct l2cap_instance_s *l2cap, + int scid, int flag, int result, const uint8_t *data, int len) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_conf_rsp *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len)); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_CONF_RSP; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len)); + + params->scid = cpu_to_le16(scid); + params->flags = cpu_to_le16(flag); + params->result = cpu_to_le16(result); + if (len) + memcpy(params->data, data, len); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap, + int dcid, int scid) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_disconn_rsp *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_DISCONN_RSP; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE); + + params->dcid = cpu_to_le16(dcid); + params->scid = cpu_to_le16(scid); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_echo_response(struct l2cap_instance_s *l2cap, + const uint8_t *data, int len) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + uint8_t *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + len); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_ECHO_RSP; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(len); + + memcpy(params, data, len); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type, + int result, const uint8_t *data, int len) +{ + uint8_t *pkt; + l2cap_cmd_hdr *hdr; + l2cap_info_rsp *params; + + pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, + L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len); + hdr = (void *) (pkt + 0); + params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); + + hdr->code = L2CAP_INFO_RSP; + hdr->ident = l2cap->last_id; + hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len); + + params->type = cpu_to_le16(type); + params->result = cpu_to_le16(result); + if (len) + memcpy(params->data, data, len); + + l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); +} + +static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len); +static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms); +#if 0 +static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len); +static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm); +#endif +static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, + const l2cap_hdr *hdr, int len); +static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, + const l2cap_hdr *hdr, int len); + +static int l2cap_cid_new(struct l2cap_instance_s *l2cap) +{ + int i; + + for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++) + if (!l2cap->cid[i]) + return i; + + return L2CAP_CID_INVALID; +} + +static inline struct bt_l2cap_psm_s *l2cap_psm( + struct bt_l2cap_device_s *device, int psm) +{ + struct bt_l2cap_psm_s *ret = device->first_psm; + + while (ret && ret->psm != psm) + ret = ret->next; + + return ret; +} + +static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap, + int psm, int source_cid) +{ + struct l2cap_chan_s *ch = 0; + struct bt_l2cap_psm_s *psm_info; + int result, status; + int cid = l2cap_cid_new(l2cap); + + if (cid) { + /* See what the channel is to be used for.. */ + psm_info = l2cap_psm(l2cap->dev, psm); + + if (psm_info) { + /* Device supports this use-case. */ + ch = qemu_mallocz(sizeof(*ch)); + ch->params.sdu_out = l2cap_bframe_out; + ch->params.sdu_submit = l2cap_bframe_submit; + ch->frame_in = l2cap_bframe_in; + ch->mps = 65536; + ch->min_mtu = MAX(48, psm_info->min_mtu); + ch->params.remote_mtu = MAX(672, ch->min_mtu); + ch->remote_cid = source_cid; + ch->mode = L2CAP_MODE_BASIC; + ch->l2cap = l2cap; + + /* Does it feel like opening yet another channel though? */ + if (!psm_info->new_channel(l2cap->dev, &ch->params)) { + l2cap->cid[cid] = ch; + + result = L2CAP_CR_SUCCESS; + status = L2CAP_CS_NO_INFO; + } else { + qemu_free(ch); + + result = L2CAP_CR_NO_MEM; + status = L2CAP_CS_NO_INFO; + } + } else { + result = L2CAP_CR_BAD_PSM; + status = L2CAP_CS_NO_INFO; + } + } else { + result = L2CAP_CR_NO_MEM; + status = L2CAP_CS_NO_INFO; + } + + l2cap_connection_response(l2cap, cid, source_cid, result, status); + + return ch; +} + +static void l2cap_channel_close(struct l2cap_instance_s *l2cap, + int cid, int source_cid) +{ + struct l2cap_chan_s *ch = 0; + + /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a + * connection in CLOSED state still responds with a L2CAP_DisconnectRsp + * message on an L2CAP_DisconnectReq event. */ + if (unlikely(cid < L2CAP_CID_ALLOC)) { + l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, + cid, source_cid); + return; + } + if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX)) + ch = l2cap->cid[cid]; + + if (likely(ch)) { + if (ch->remote_cid != source_cid) { + fprintf(stderr, "%s: Ignoring a Disconnection Request with the " + "invalid SCID %04x.\n", __FUNCTION__, source_cid); + return; + } + + l2cap->cid[cid] = 0; + + ch->params.close(ch->params.opaque); + qemu_free(ch); + } + + l2cap_disconnection_response(l2cap, cid, source_cid); +} + +static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap, + struct l2cap_chan_s *ch) +{ + l2cap_configuration_request(l2cap, ch->remote_cid, 0, 0, 0); + ch->config_req_id = l2cap->last_id; + ch->config &= ~L2CAP_CFG_INIT; +} + +static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap, + struct l2cap_chan_s *ch) +{ + /* Use all default channel options and terminate negotiation. */ + l2cap_channel_config_null(l2cap, ch); +} + +static int l2cap_channel_config(struct l2cap_instance_s *l2cap, + struct l2cap_chan_s *ch, int flag, + const uint8_t *data, int len) +{ + l2cap_conf_opt *opt; + l2cap_conf_opt_qos *qos; + uint32_t val; + uint8_t rsp[len]; + int result = L2CAP_CONF_SUCCESS; + + data = memcpy(rsp, data, len); + while (len) { + opt = (void *) data; + + if (len < L2CAP_CONF_OPT_SIZE || + len < L2CAP_CONF_OPT_SIZE + opt->len) { + result = L2CAP_CONF_REJECT; + break; + } + data += L2CAP_CONF_OPT_SIZE + opt->len; + len -= L2CAP_CONF_OPT_SIZE + opt->len; + + switch (opt->type & 0x7f) { + case L2CAP_CONF_MTU: + if (opt->len != 2) { + result = L2CAP_CONF_REJECT; + break; + } + + /* MTU */ + val = le16_to_cpup((void *) opt->val); + if (val < ch->min_mtu) { + cpu_to_le16w((void *) opt->val, ch->min_mtu); + result = L2CAP_CONF_UNACCEPT; + break; + } + + ch->params.remote_mtu = val; + break; + + case L2CAP_CONF_FLUSH_TO: + if (opt->len != 2) { + result = L2CAP_CONF_REJECT; + break; + } + + /* Flush Timeout */ + val = le16_to_cpup((void *) opt->val); + if (val < 0x0001) { + opt->val[0] = 0xff; + opt->val[1] = 0xff; + result = L2CAP_CONF_UNACCEPT; + break; + } + break; + + case L2CAP_CONF_QOS: + if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) { + result = L2CAP_CONF_REJECT; + break; + } + qos = (void *) opt->val; + + /* Flags */ + val = qos->flags; + if (val) { + qos->flags = 0; + result = L2CAP_CONF_UNACCEPT; + } + + /* Service type */ + val = qos->service_type; + if (val != L2CAP_CONF_QOS_BEST_EFFORT && + val != L2CAP_CONF_QOS_NO_TRAFFIC) { + qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT; + result = L2CAP_CONF_UNACCEPT; + } + + if (val != L2CAP_CONF_QOS_NO_TRAFFIC) { + /* XXX: These values should possibly be calculated + * based on LM / baseband properties also. */ + + /* Token rate */ + val = le32_to_cpu(qos->token_rate); + if (val == L2CAP_CONF_QOS_WILDCARD) + qos->token_rate = cpu_to_le32(0x100000); + + /* Token bucket size */ + val = le32_to_cpu(qos->token_bucket_size); + if (val == L2CAP_CONF_QOS_WILDCARD) + qos->token_bucket_size = cpu_to_le32(65500); + + /* Any Peak bandwidth value is correct to return as-is */ + /* Any Access latency value is correct to return as-is */ + /* Any Delay variation value is correct to return as-is */ + } + break; + + case L2CAP_CONF_RFC: + if (opt->len != 9) { + result = L2CAP_CONF_REJECT; + break; + } + + /* Mode */ + val = opt->val[0]; + switch (val) { + case L2CAP_MODE_BASIC: + ch->mode = val; + ch->frame_in = l2cap_bframe_in; + + /* All other parameters shall be ignored */ + break; + + case L2CAP_MODE_RETRANS: + case L2CAP_MODE_FLOWCTL: + ch->mode = val; + ch->frame_in = l2cap_iframe_in; + /* Note: most of these parameters refer to incoming traffic + * so we don't need to save them as long as we can accept + * incoming PDUs at any values of the parameters. */ + + /* TxWindow size */ + val = opt->val[1]; + if (val < 1 || val > 32) { + opt->val[1] = 32; + result = L2CAP_CONF_UNACCEPT; + break; + } + + /* MaxTransmit */ + val = opt->val[2]; + if (val < 1) { + opt->val[2] = 1; + result = L2CAP_CONF_UNACCEPT; + break; + } + + /* Remote Retransmission time-out shouldn't affect local + * operation (?) */ + + /* The Monitor time-out drives the local Monitor timer (?), + * so save the value. */ + val = (opt->val[6] << 8) | opt->val[5]; + if (val < 30) { + opt->val[5] = 100 & 0xff; + opt->val[6] = 100 >> 8; + result = L2CAP_CONF_UNACCEPT; + break; + } + ch->monitor_timeout = val; + l2cap_monitor_timer_update(ch); + + /* MPS */ + val = (opt->val[8] << 8) | opt->val[7]; + if (val < ch->min_mtu) { + opt->val[7] = ch->min_mtu & 0xff; + opt->val[8] = ch->min_mtu >> 8; + result = L2CAP_CONF_UNACCEPT; + break; + } + ch->mps = val; + break; + + default: + result = L2CAP_CONF_UNACCEPT; + break; + } + break; + + default: + if (!(opt->type >> 7)) + result = L2CAP_CONF_UNKNOWN; + break; + } + + if (result != L2CAP_CONF_SUCCESS) + break; /* XXX: should continue? */ + } + + l2cap_configuration_response(l2cap, ch->remote_cid, + flag, result, rsp, len); + + return result == L2CAP_CONF_SUCCESS && !flag; +} + +static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap, + int flag, int cid, const uint8_t *data, int len) +{ + struct l2cap_chan_s *ch; + + if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { + l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, + cid, 0x0000); + return; + } + ch = l2cap->cid[cid]; + + /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to + * WAIT_CONFIG_REQ_RSP. This is assuming the transition chart for OPEN + * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake + * and on options-acceptable we go back to OPEN and otherwise to + * WAIT_CONFIG_REQ and not the other way. */ + ch->config &= ~L2CAP_CFG_ACC; + + if (l2cap_channel_config(l2cap, ch, flag, data, len)) + /* Go to OPEN or WAIT_CONFIG_RSP */ + ch->config |= L2CAP_CFG_ACC; + + /* TODO: if the incoming traffic flow control or retransmission mode + * changed then we probably need to also generate the + * ConfigureChannel_Req event and set the outgoing traffic to the same + * mode. */ + if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) && + !ch->config_req_id) + l2cap_channel_config_req_event(l2cap, ch); +} + +static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap, + int result, int flag, int cid, const uint8_t *data, int len) +{ + struct l2cap_chan_s *ch; + + if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { + l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, + cid, 0x0000); + return 0; + } + ch = l2cap->cid[cid]; + + if (ch->config_req_id != l2cap->last_id) + return 1; + ch->config_req_id = 0; + + if (result == L2CAP_CONF_SUCCESS) { + if (!flag) + ch->config |= L2CAP_CFG_INIT; + else + l2cap_channel_config_null(l2cap, ch); + } else + /* Retry until we succeed */ + l2cap_channel_config_req_event(l2cap, ch); + + return 0; +} + +static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap, + int psm, int source_cid) +{ + struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid); + + if (!ch) + return; + + /* Optional */ + if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id) + l2cap_channel_config_req_event(l2cap, ch); +} + +static void l2cap_info(struct l2cap_instance_s *l2cap, int type) +{ + uint8_t data[4]; + int len = 0; + int result = L2CAP_IR_SUCCESS; + + switch (type) { + case L2CAP_IT_CL_MTU: + data[len ++] = l2cap->group_ch.mps & 0xff; + data[len ++] = l2cap->group_ch.mps >> 8; + break; + + case L2CAP_IT_FEAT_MASK: + /* (Prematurely) report Flow control and Retransmission modes. */ + data[len ++] = 0x03; + data[len ++] = 0x00; + data[len ++] = 0x00; + data[len ++] = 0x00; + break; + + default: + result = L2CAP_IR_NOTSUPP; + } + + l2cap_info_response(l2cap, type, result, data, len); +} + +static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id, + const uint8_t *params, int len) +{ + int err; + +#if 0 + /* TODO: do the IDs really have to be in sequence? */ + if (!id || (id != l2cap->last_id && id != l2cap->next_id)) { + fprintf(stderr, "%s: out of sequence command packet ignored.\n", + __FUNCTION__); + return; + } +#else + l2cap->next_id = id; +#endif + if (id == l2cap->next_id) { + l2cap->last_id = l2cap->next_id; + l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; + } else { + /* TODO: Need to re-send the same response, without re-executing + * the corresponding command! */ + } + + switch (code) { + case L2CAP_COMMAND_REJ: + if (unlikely(len != 2 && len != 4 && len != 6)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + /* We never issue commands other than Command Reject currently. */ + fprintf(stderr, "%s: stray Command Reject (%02x, %04x) " + "packet, ignoring.\n", __FUNCTION__, id, + le16_to_cpu(((l2cap_cmd_rej *) params)->reason)); + break; + + case L2CAP_CONN_REQ: + if (unlikely(len != L2CAP_CONN_REQ_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + l2cap_channel_open_req_msg(l2cap, + le16_to_cpu(((l2cap_conn_req *) params)->psm), + le16_to_cpu(((l2cap_conn_req *) params)->scid)); + break; + + case L2CAP_CONN_RSP: + if (unlikely(len != L2CAP_CONN_RSP_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + /* We never issue Connection Requests currently. TODO */ + fprintf(stderr, "%s: unexpected Connection Response (%02x) " + "packet, ignoring.\n", __FUNCTION__, id); + break; + + case L2CAP_CONF_REQ: + if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + l2cap_channel_config_req_msg(l2cap, + le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1, + le16_to_cpu(((l2cap_conf_req *) params)->dcid), + ((l2cap_conf_req *) params)->data, + len - L2CAP_CONF_REQ_SIZE(0)); + break; + + case L2CAP_CONF_RSP: + if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + if (l2cap_channel_config_rsp_msg(l2cap, + le16_to_cpu(((l2cap_conf_rsp *) params)->result), + le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1, + le16_to_cpu(((l2cap_conf_rsp *) params)->scid), + ((l2cap_conf_rsp *) params)->data, + len - L2CAP_CONF_RSP_SIZE(0))) + fprintf(stderr, "%s: unexpected Configure Response (%02x) " + "packet, ignoring.\n", __FUNCTION__, id); + break; + + case L2CAP_DISCONN_REQ: + if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + l2cap_channel_close(l2cap, + le16_to_cpu(((l2cap_disconn_req *) params)->dcid), + le16_to_cpu(((l2cap_disconn_req *) params)->scid)); + break; + + case L2CAP_DISCONN_RSP: + if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + /* We never issue Disconnection Requests currently. TODO */ + fprintf(stderr, "%s: unexpected Disconnection Response (%02x) " + "packet, ignoring.\n", __FUNCTION__, id); + break; + + case L2CAP_ECHO_REQ: + l2cap_echo_response(l2cap, params, len); + break; + + case L2CAP_ECHO_RSP: + /* We never issue Echo Requests currently. TODO */ + fprintf(stderr, "%s: unexpected Echo Response (%02x) " + "packet, ignoring.\n", __FUNCTION__, id); + break; + + case L2CAP_INFO_REQ: + if (unlikely(len != L2CAP_INFO_REQ_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type)); + break; + + case L2CAP_INFO_RSP: + if (unlikely(len != L2CAP_INFO_RSP_SIZE)) { + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + goto reject; + } + + /* We never issue Information Requests currently. TODO */ + fprintf(stderr, "%s: unexpected Information Response (%02x) " + "packet, ignoring.\n", __FUNCTION__, id); + break; + + default: + err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; + reject: + l2cap_command_reject(l2cap, id, err, 0, 0); + break; + } +} + +static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable) +{ + ch->rexmit = enable; + + l2cap_retransmission_timer_update(ch); + l2cap_monitor_timer_update(ch); +} + +/* Command frame SDU */ +static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len) +{ + struct l2cap_instance_s *l2cap = opaque; + const l2cap_cmd_hdr *hdr; + int clen; + + while (len) { + hdr = (void *) data; + if (len < L2CAP_CMD_HDR_SIZE) + /* TODO: signal an error */ + return; + len -= L2CAP_CMD_HDR_SIZE; + data += L2CAP_CMD_HDR_SIZE; + + clen = le16_to_cpu(hdr->len); + if (len < clen) { + l2cap_command_reject(l2cap, hdr->ident, + L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0); + break; + } + + l2cap_command(l2cap, hdr->code, hdr->ident, data, clen); + len -= clen; + data += clen; + } +} + +/* Group frame SDU */ +static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len) +{ +} + +/* Supervisory frame */ +static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl) +{ +} + +/* Basic L2CAP mode Information frame */ +static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, + const l2cap_hdr *hdr, int len) +{ + /* We have a full SDU, no further processing */ + ch->params.sdu_in(ch->params.opaque, hdr->data, len); +} + +/* Flow Control and Retransmission mode frame */ +static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, + const l2cap_hdr *hdr, int len) +{ + uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2)); + + if (len < 4) + goto len_error; + if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs) + goto fcs_error; + + if ((hdr->data[0] >> 7) == ch->rexmit) + l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7)); + + if (hdr->data[0] & 1) { + if (len != 4) + /* TODO: Signal an error? */; + return; + + return l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data)); + } + + switch (hdr->data[1] >> 6) { /* SAR */ + case L2CAP_SAR_NO_SEG: + if (ch->len_total) + goto seg_error; + if (len - 4 > ch->mps) + goto len_error; + + return ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4); + + case L2CAP_SAR_START: + if (ch->len_total || len < 6) + goto seg_error; + if (len - 6 > ch->mps) + goto len_error; + + ch->len_total = le16_to_cpup((void *) (hdr->data + 2)); + if (len >= 6 + ch->len_total) + goto seg_error; + + ch->len_cur = len - 6; + memcpy(ch->sdu, hdr->data + 4, ch->len_cur); + break; + + case L2CAP_SAR_END: + if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total) + goto seg_error; + if (len - 4 > ch->mps) + goto len_error; + + memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); + return ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total); + + case L2CAP_SAR_CONT: + if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total) + goto seg_error; + if (len - 4 > ch->mps) + goto len_error; + + memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); + ch->len_cur += len - 4; + break; + + seg_error: + len_error: /* TODO */ + fcs_error: /* TODO */ + ch->len_cur = 0; + ch->len_total = 0; + break; + } +} + +static void l2cap_frame_in(struct l2cap_instance_s *l2cap, + const l2cap_hdr *frame) +{ + uint16_t cid = le16_to_cpu(frame->cid); + uint16_t len = le16_to_cpu(frame->len); + + if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { + fprintf(stderr, "%s: frame addressed to a non-existent L2CAP " + "channel %04x received.\n", __FUNCTION__, cid); + return; + } + + l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len); +} + +/* "Recombination" */ +static void l2cap_pdu_in(struct l2cap_instance_s *l2cap, + const uint8_t *data, int len) +{ + const l2cap_hdr *hdr = (void *) l2cap->frame_in; + + if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) { + if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) { + memcpy(l2cap->frame_in + l2cap->frame_in_len, data, + sizeof(l2cap->frame_in) - l2cap->frame_in_len); + l2cap->frame_in_len = sizeof(l2cap->frame_in); + /* TODO: truncate */ + l2cap_frame_in(l2cap, hdr); + } + + return; + } + + memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len); + l2cap->frame_in_len += len; + + if (len >= L2CAP_HDR_SIZE) + if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len)) + l2cap_frame_in(l2cap, hdr); + /* There is never a start of a new PDU in the same ACL packet, so + * no need to memmove the remaining payload and loop. */ +} + +static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap, + uint16_t cid, uint16_t len) +{ + l2cap_hdr *hdr = (void *) l2cap->frame_out; + + l2cap->frame_out_len = len + L2CAP_HDR_SIZE; + + hdr->cid = cpu_to_le16(cid); + hdr->len = cpu_to_le16(len); + + return l2cap->frame_out + L2CAP_HDR_SIZE; +} + +static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap) +{ + /* TODO: Fragmentation */ + (l2cap->role ? + l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp) + (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len); +} + +static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len) +{ + struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; + + if (len > chan->params.remote_mtu) { + fprintf(stderr, "%s: B-Frame for CID %04x longer than %i octets.\n", + __FUNCTION__, + chan->remote_cid, chan->params.remote_mtu); + exit(-1); + } + + return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len); +} + +static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms) +{ + struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms; + + return l2cap_pdu_submit(chan->l2cap); +} + +#if 0 +/* Stub: Only used if an emulated device requests outgoing flow control */ +static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len) +{ + struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; + + if (len > chan->params.remote_mtu) { + /* TODO: slice into segments and queue each segment as a separate + * I-Frame in a FIFO of I-Frames, local to the CID. */ + } else { + /* TODO: add to the FIFO of I-Frames, local to the CID. */ + /* Possibly we need to return a pointer to a contiguous buffer + * for now and then memcpy from it into FIFOs in l2cap_iframe_submit + * while segmenting at the same time. */ + } + return 0; +} + +static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm) +{ + /* TODO: If flow control indicates clear to send, start submitting the + * invidual I-Frames from the FIFO, but don't remove them from there. + * Kick the appropriate timer until we get an S-Frame, and only then + * remove from FIFO or resubmit and re-kick the timer if the timer + * expired. */ +} +#endif + +static void l2cap_init(struct l2cap_instance_s *l2cap, + struct bt_link_s *link, int role) +{ + l2cap->link = link; + l2cap->role = role; + l2cap->dev = (struct bt_l2cap_device_s *) + (role ? link->host : link->slave); + + l2cap->next_id = 1; + + /* Establish the signalling channel */ + l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in; + l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out; + l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit; + l2cap->signalling_ch.params.opaque = l2cap; + l2cap->signalling_ch.params.remote_mtu = 48; + l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING; + l2cap->signalling_ch.frame_in = l2cap_bframe_in; + l2cap->signalling_ch.mps = 65536; + l2cap->signalling_ch.min_mtu = 48; + l2cap->signalling_ch.mode = L2CAP_MODE_BASIC; + l2cap->signalling_ch.l2cap = l2cap; + l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch; + + /* Establish the connection-less data channel */ + l2cap->group_ch.params.sdu_in = l2cap_gframe_in; + l2cap->group_ch.params.opaque = l2cap; + l2cap->group_ch.frame_in = l2cap_bframe_in; + l2cap->group_ch.mps = 65533; + l2cap->group_ch.l2cap = l2cap; + l2cap->group_ch.remote_cid = L2CAP_CID_INVALID; + l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch; +} + +static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect) +{ + int cid; + + /* Don't send DISCONNECT if we are currently handling a DISCONNECT + * sent from the other side. */ + if (send_disconnect) { + if (l2cap->role) + l2cap->dev->device.lmp_disconnect_slave(l2cap->link); + /* l2cap->link is invalid from now on. */ + else + l2cap->dev->device.lmp_disconnect_master(l2cap->link); + } + + for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++) + if (l2cap->cid[cid]) { + l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque); + free(l2cap->cid[cid]); + } + + if (l2cap->role) + qemu_free(l2cap); + else + qemu_free(l2cap->link); +} + +/* L2CAP glue to lower layers in bluetooth stack (LMP) */ + +static void l2cap_lmp_connection_request(struct bt_link_s *link) +{ + struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave; + struct slave_l2cap_instance_s *l2cap; + + /* Always accept - we only get called if (dev->device->page_scan). */ + + l2cap = qemu_mallocz(sizeof(struct slave_l2cap_instance_s)); + l2cap->link.slave = &dev->device; + l2cap->link.host = link->host; + l2cap_init(&l2cap->l2cap, &l2cap->link, 0); + + /* Always at the end */ + link->host->reject_reason = 0; + link->host->lmp_connection_complete(&l2cap->link); +} + +/* Stub */ +static void l2cap_lmp_connection_complete(struct bt_link_s *link) +{ + struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; + struct l2cap_instance_s *l2cap; + + if (dev->device.reject_reason) { + /* Signal to upper layer */ + return; + } + + l2cap = qemu_mallocz(sizeof(struct l2cap_instance_s)); + l2cap_init(l2cap, link, 1); + + link->acl_mode = acl_active; + + /* Signal to upper layer */ +} + +/* Stub */ +static void l2cap_lmp_disconnect_host(struct bt_link_s *link) +{ + struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; + struct l2cap_instance_s *l2cap = + /* TODO: Retrieve from upper layer */ (void *) dev; + + /* Signal to upper layer */ + + l2cap_teardown(l2cap, 0); +} + +static void l2cap_lmp_disconnect_slave(struct bt_link_s *link) +{ + struct slave_l2cap_instance_s *l2cap = + (struct slave_l2cap_instance_s *) link; + + l2cap_teardown(&l2cap->l2cap, 0); +} + +static void l2cap_lmp_acl_data_slave(struct bt_link_s *link, + const uint8_t *data, int start, int len) +{ + struct slave_l2cap_instance_s *l2cap = + (struct slave_l2cap_instance_s *) link; + + if (start) + l2cap->l2cap.frame_in_len = 0; + + l2cap_pdu_in(&l2cap->l2cap, data, len); +} + +/* Stub */ +static void l2cap_lmp_acl_data_host(struct bt_link_s *link, + const uint8_t *data, int start, int len) +{ + struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; + struct l2cap_instance_s *l2cap = + /* TODO: Retrieve from upper layer */ (void *) dev; + + if (start) + l2cap->frame_in_len = 0; + + l2cap_pdu_in(l2cap, data, len); +} + +static void l2cap_dummy_destroy(struct bt_device_s *dev) +{ + struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev; + + bt_l2cap_device_done(l2cap_dev); +} + +void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, + struct bt_scatternet_s *net) +{ + bt_device_init(&dev->device, net); + + dev->device.lmp_connection_request = l2cap_lmp_connection_request; + dev->device.lmp_connection_complete = l2cap_lmp_connection_complete; + dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host; + dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave; + dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave; + dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host; + + dev->device.handle_destroy = l2cap_dummy_destroy; +} + +void bt_l2cap_device_done(struct bt_l2cap_device_s *dev) +{ + bt_device_done(&dev->device); + + /* Should keep a list of all instances and go through it and + * invoke l2cap_teardown() for each. */ +} + +void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu, + int (*new_channel)(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params)) +{ + struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm); + + if (new_psm) { + fprintf(stderr, "%s: PSM %04x already registered for device `%s'.\n", + __FUNCTION__, psm, dev->device.lmp_name); + exit(-1); + } + + new_psm = qemu_mallocz(sizeof(*new_psm)); + new_psm->psm = psm; + new_psm->min_mtu = min_mtu; + new_psm->new_channel = new_channel; + new_psm->next = dev->first_psm; + dev->first_psm = new_psm; +} diff -Nur kvm-76/qemu/hw/bt-sdp.c kvm-userspace/qemu/hw/bt-sdp.c --- kvm-76/qemu/hw/bt-sdp.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt-sdp.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,969 @@ +/* + * Service Discover Protocol server for QEMU L2CAP devices + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "bt.h" + +struct bt_l2cap_sdp_state_s { + struct bt_l2cap_conn_params_s *channel; + + struct sdp_service_record_s { + int match; + + int *uuid; + int uuids; + struct sdp_service_attribute_s { + int match; + + int attribute_id; + int len; + void *pair; + } *attribute_list; + int attributes; + } *service_list; + int services; +}; + +static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) +{ + size_t len = *(*element) ++ & SDP_DSIZE_MASK; + + if (!*left) + return -1; + (*left) --; + + if (len < SDP_DSIZE_NEXT1) + return 1 << len; + else if (len == SDP_DSIZE_NEXT1) { + if (*left < 1) + return -1; + (*left) --; + + return *(*element) ++; + } else if (len == SDP_DSIZE_NEXT2) { + if (*left < 2) + return -1; + (*left) -= 2; + + len = (*(*element) ++) << 8; + return len | (*(*element) ++); + } else { + if (*left < 4) + return -1; + (*left) -= 4; + + len = (*(*element) ++) << 24; + len |= (*(*element) ++) << 16; + len |= (*(*element) ++) << 8; + return len | (*(*element) ++); + } +} + +static const uint8_t bt_base_uuid[12] = { + 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, +}; + +static int sdp_uuid_match(struct sdp_service_record_s *record, + const uint8_t *uuid, ssize_t datalen) +{ + int *lo, hi, val; + + if (datalen == 16 || datalen == 4) { + if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12)) + return 0; + + if (uuid[0] | uuid[1]) + return 0; + uuid += 2; + } + + val = (uuid[0] << 8) | uuid[1]; + lo = record->uuid; + hi = record->uuids; + while (hi >>= 1) + if (lo[hi] <= val) + lo += hi; + + return *lo == val; +} + +#define CONTINUATION_PARAM_SIZE (1 + sizeof(int)) +#define MAX_PDU_OUT_SIZE 96 /* Arbitrary */ +#define PDU_HEADER_SIZE 5 +#define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \ + CONTINUATION_PARAM_SIZE) + +static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp, + const uint8_t **req, ssize_t *len) +{ + size_t datalen; + int i; + + if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID) + return 1; + + datalen = sdp_datalen(req, len); + if (datalen != 2 && datalen != 4 && datalen != 16) + return 1; + + for (i = 0; i < sdp->services; i ++) + if (sdp_uuid_match(&sdp->service_list[i], *req, datalen)) + sdp->service_list[i].match = 1; + + (*req) += datalen; + (*len) -= datalen; + + return 0; +} + +static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, + uint8_t *rsp, const uint8_t *req, ssize_t len) +{ + ssize_t seqlen; + int i, count, start, end, max; + int32_t handle; + + /* Perform the search */ + for (i = 0; i < sdp->services; i ++) + sdp->service_list[i].match = 0; + + if (len < 1) + return -SDP_INVALID_SYNTAX; + if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { + seqlen = sdp_datalen(&req, &len); + if (seqlen < 3 || len < seqlen) + return -SDP_INVALID_SYNTAX; + len -= seqlen; + + while (seqlen) + if (sdp_svc_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + } else if (sdp_svc_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + + if (len < 3) + return -SDP_INVALID_SYNTAX; + end = (req[0] << 8) | req[1]; + req += 2; + len -= 2; + + if (*req) { + if (len <= sizeof(int)) + return -SDP_INVALID_SYNTAX; + len -= sizeof(int); + memcpy(&start, req + 1, sizeof(int)); + } else + start = 0; + + if (len > 1); + return -SDP_INVALID_SYNTAX; + + /* Output the results */ + len = 4; + count = 0; + end = start; + for (i = 0; i < sdp->services; i ++) + if (sdp->service_list[i].match) { + if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) { + handle = i; + memcpy(rsp + len, &handle, 4); + len += 4; + end = count + 1; + } + + count ++; + } + + rsp[0] = count >> 8; + rsp[1] = count & 0xff; + rsp[2] = (end - start) >> 8; + rsp[3] = (end - start) & 0xff; + + if (end < count) { + rsp[len ++] = sizeof(int); + memcpy(rsp + len, &end, sizeof(int)); + len += 4; + } else + rsp[len ++] = 0; + + return len; +} + +static int sdp_attr_match(struct sdp_service_record_s *record, + const uint8_t **req, ssize_t *len) +{ + int i, start, end; + + if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { + (*req) ++; + if (*len < 3) + return 1; + + start = (*(*req) ++) << 8; + start |= *(*req) ++; + end = start; + *len -= 3; + } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { + (*req) ++; + if (*len < 5) + return 1; + + start = (*(*req) ++) << 8; + start |= *(*req) ++; + end = (*(*req) ++) << 8; + end |= *(*req) ++; + *len -= 5; + } else + return 1; + + for (i = 0; i < record->attributes; i ++) + if (record->attribute_list[i].attribute_id >= start && + record->attribute_list[i].attribute_id <= end) + record->attribute_list[i].match = 1; + + return 0; +} + +static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, + uint8_t *rsp, const uint8_t *req, ssize_t len) +{ + ssize_t seqlen; + int i, start, end, max; + int32_t handle; + struct sdp_service_record_s *record; + uint8_t *lst; + + /* Perform the search */ + if (len < 7) + return -SDP_INVALID_SYNTAX; + memcpy(&handle, req, 6); + req += 4; + len -= 4; + + if (handle < 0 || handle > sdp->services) + return -SDP_INVALID_RECORD_HANDLE; + record = &sdp->service_list[handle]; + + for (i = 0; i < record->attributes; i ++) + record->attribute_list[i].match = 0; + + max = (req[0] << 8) | req[1]; + req += 2; + len -= 2; + if (max < 0x0007) + return -SDP_INVALID_SYNTAX; + + if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { + seqlen = sdp_datalen(&req, &len); + if (seqlen < 3 || len < seqlen) + return -SDP_INVALID_SYNTAX; + len -= seqlen; + + while (seqlen) + if (sdp_attr_match(record, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + } else if (sdp_attr_match(record, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + + if (len < 1) + return -SDP_INVALID_SYNTAX; + + if (*req) { + if (len <= sizeof(int)) + return -SDP_INVALID_SYNTAX; + len -= sizeof(int); + memcpy(&start, req + 1, sizeof(int)); + } else + start = 0; + + if (len > 1) + return -SDP_INVALID_SYNTAX; + + /* Output the results */ + lst = rsp + 2; + max = MIN(max, MAX_RSP_PARAM_SIZE); + len = 3 - start; + end = 0; + for (i = 0; i < record->attributes; i ++) + if (record->attribute_list[i].match) { + if (len >= 0 && len + record->attribute_list[i].len < max) { + memcpy(lst + len, record->attribute_list[i].pair, + record->attribute_list[i].len); + end = len + record->attribute_list[i].len; + } + len += record->attribute_list[i].len; + } + if (0 >= start) { + lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; + lst[1] = (len + start - 3) >> 8; + lst[2] = (len + start - 3) & 0xff; + } + + rsp[0] = end >> 8; + rsp[1] = end & 0xff; + + if (end < len) { + len = end + start; + lst[end ++] = sizeof(int); + memcpy(lst + end, &len, sizeof(int)); + end += sizeof(int); + } else + lst[end ++] = 0; + + return end + 2; +} + +static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp, + const uint8_t **req, ssize_t *len) +{ + int i, j, start, end; + struct sdp_service_record_s *record; + + if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { + (*req) ++; + if (*len < 3) + return 1; + + start = (*(*req) ++) << 8; + start |= *(*req) ++; + end = start; + *len -= 3; + } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { + (*req) ++; + if (*len < 5) + return 1; + + start = (*(*req) ++) << 8; + start |= *(*req) ++; + end = (*(*req) ++) << 8; + end |= *(*req) ++; + *len -= 5; + } else + return 1; + + for (i = 0; i < sdp->services; i ++) + if ((record = &sdp->service_list[i])->match) + for (j = 0; j < record->attributes; j ++) + if (record->attribute_list[j].attribute_id >= start && + record->attribute_list[j].attribute_id <= end) + record->attribute_list[j].match = 1; + + return 0; +} + +static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, + uint8_t *rsp, const uint8_t *req, ssize_t len) +{ + ssize_t seqlen; + int i, j, start, end, max; + struct sdp_service_record_s *record; + uint8_t *lst; + + /* Perform the search */ + for (i = 0; i < sdp->services; i ++) { + sdp->service_list[i].match = 0; + for (j = 0; j < sdp->service_list[i].attributes; j ++) + sdp->service_list[i].attribute_list[j].match = 0; + } + + if (len < 1) + return -SDP_INVALID_SYNTAX; + if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { + seqlen = sdp_datalen(&req, &len); + if (seqlen < 3 || len < seqlen) + return -SDP_INVALID_SYNTAX; + len -= seqlen; + + while (seqlen) + if (sdp_svc_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + } else if (sdp_svc_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + + if (len < 3) + return -SDP_INVALID_SYNTAX; + max = (req[0] << 8) | req[1]; + req += 2; + len -= 2; + if (max < 0x0007) + return -SDP_INVALID_SYNTAX; + + if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { + seqlen = sdp_datalen(&req, &len); + if (seqlen < 3 || len < seqlen) + return -SDP_INVALID_SYNTAX; + len -= seqlen; + + while (seqlen) + if (sdp_svc_attr_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + } else if (sdp_svc_attr_match(sdp, &req, &seqlen)) + return -SDP_INVALID_SYNTAX; + + if (len < 1) + return -SDP_INVALID_SYNTAX; + + if (*req) { + if (len <= sizeof(int)) + return -SDP_INVALID_SYNTAX; + len -= sizeof(int); + memcpy(&start, req + 1, sizeof(int)); + } else + start = 0; + + if (len > 1) + return -SDP_INVALID_SYNTAX; + + /* Output the results */ + /* This assumes empty attribute lists are never to be returned even + * for matching Service Records. In practice this shouldn't happen + * as the requestor will usually include the always present + * ServiceRecordHandle AttributeID in AttributeIDList. */ + lst = rsp + 2; + max = MIN(max, MAX_RSP_PARAM_SIZE); + len = 3 - start; + end = 0; + for (i = 0; i < sdp->services; i ++) + if ((record = &sdp->service_list[i])->match) { + len += 3; + seqlen = len; + for (j = 0; j < record->attributes; j ++) + if (record->attribute_list[j].match) { + if (len >= 0) + if (len + record->attribute_list[j].len < max) { + memcpy(lst + len, record->attribute_list[j].pair, + record->attribute_list[j].len); + end = len + record->attribute_list[j].len; + } + len += record->attribute_list[j].len; + } + if (seqlen == len) + len -= 3; + else if (seqlen >= 3 && seqlen < max) { + lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; + lst[seqlen - 2] = (len - seqlen) >> 8; + lst[seqlen - 1] = (len - seqlen) & 0xff; + } + } + if (len == 3 - start) + len -= 3; + else if (0 >= start) { + lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; + lst[1] = (len + start - 3) >> 8; + lst[2] = (len + start - 3) & 0xff; + } + + rsp[0] = end >> 8; + rsp[1] = end & 0xff; + + if (end < len) { + len = end + start; + lst[end ++] = sizeof(int); + memcpy(lst + end, &len, sizeof(int)); + end += sizeof(int); + } else + lst[end ++] = 0; + + return end + 2; +} + +static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len) +{ + struct bt_l2cap_sdp_state_s *sdp = opaque; + enum bt_sdp_cmd pdu_id; + uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out; + int transaction_id, plen; + int err = 0; + int rsp_len = 0; + + if (len < 5) { + fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len); + return; + } + + pdu_id = *data ++; + transaction_id = (data[0] << 8) | data[1]; + plen = (data[2] << 8) | data[3]; + data += 4; + len -= 5; + + if (len != plen) { + fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n", + __FUNCTION__, plen, len); + err = SDP_INVALID_PDU_SIZE; + goto respond; + } + + switch (pdu_id) { + case SDP_SVC_SEARCH_REQ: + rsp_len = sdp_svc_search(sdp, rsp, data, len); + pdu_id = SDP_SVC_SEARCH_RSP; + break; + + case SDP_SVC_ATTR_REQ: + rsp_len = sdp_attr_get(sdp, rsp, data, len); + pdu_id = SDP_SVC_ATTR_RSP; + break; + + case SDP_SVC_SEARCH_ATTR_REQ: + rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len); + pdu_id = SDP_SVC_SEARCH_ATTR_RSP; + break; + + case SDP_ERROR_RSP: + case SDP_SVC_ATTR_RSP: + case SDP_SVC_SEARCH_RSP: + case SDP_SVC_SEARCH_ATTR_RSP: + default: + fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n", + __FUNCTION__, pdu_id); + err = SDP_INVALID_SYNTAX; + break; + } + + if (rsp_len < 0) { + err = -rsp_len; + rsp_len = 0; + } + +respond: + if (err) { + pdu_id = SDP_ERROR_RSP; + rsp[rsp_len ++] = err >> 8; + rsp[rsp_len ++] = err & 0xff; + } + + sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE); + + sdu_out[0] = pdu_id; + sdu_out[1] = transaction_id >> 8; + sdu_out[2] = transaction_id & 0xff; + sdu_out[3] = rsp_len >> 8; + sdu_out[4] = rsp_len & 0xff; + memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len); + + sdp->channel->sdu_submit(sdp->channel); +} + +static void bt_l2cap_sdp_close_ch(void *opaque) +{ + struct bt_l2cap_sdp_state_s *sdp = opaque; + int i; + + for (i = 0; i < sdp->services; i ++) { + qemu_free(sdp->service_list[i].attribute_list->pair); + qemu_free(sdp->service_list[i].attribute_list); + qemu_free(sdp->service_list[i].uuid); + } + qemu_free(sdp->service_list); + qemu_free(sdp); +} + +struct sdp_def_service_s { + uint16_t class_uuid; + struct sdp_def_attribute_s { + uint16_t id; + struct sdp_def_data_element_s { + uint8_t type; + union { + uint32_t uint; + const char *str; + struct sdp_def_data_element_s *list; + } value; + } data; + } attributes[]; +}; + +/* Calculate a safe byte count to allocate that will store the given + * element, at the same time count elements of a UUID type. */ +static int sdp_attr_max_size(struct sdp_def_data_element_s *element, + int *uuids) +{ + int type = element->type & ~SDP_DSIZE_MASK; + int len; + + if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID || + type == SDP_DTYPE_BOOL) { + if (type == SDP_DTYPE_UUID) + (*uuids) ++; + return 1 + (1 << (element->type & SDP_DSIZE_MASK)); + } + + if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { + if (element->type & SDP_DSIZE_MASK) { + for (len = 0; element->value.str[len] | + element->value.str[len + 1]; len ++); + return len; + } else + return 2 + strlen(element->value.str); + } + + if (type != SDP_DTYPE_SEQ) + exit(-1); + len = 2; + element = element->value.list; + while (element->type) + len += sdp_attr_max_size(element ++, uuids); + if (len > 255) + exit (-1); + + return len; +} + +static int sdp_attr_write(uint8_t *data, + struct sdp_def_data_element_s *element, int **uuid) +{ + int type = element->type & ~SDP_DSIZE_MASK; + int len = 0; + + if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) { + data[len ++] = element->type; + if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1) + data[len ++] = (element->value.uint >> 0) & 0xff; + else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) { + data[len ++] = (element->value.uint >> 8) & 0xff; + data[len ++] = (element->value.uint >> 0) & 0xff; + } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) { + data[len ++] = (element->value.uint >> 24) & 0xff; + data[len ++] = (element->value.uint >> 16) & 0xff; + data[len ++] = (element->value.uint >> 8) & 0xff; + data[len ++] = (element->value.uint >> 0) & 0xff; + } + + return len; + } + + if (type == SDP_DTYPE_UUID) { + *(*uuid) ++ = element->value.uint; + + data[len ++] = element->type; + data[len ++] = (element->value.uint >> 24) & 0xff; + data[len ++] = (element->value.uint >> 16) & 0xff; + data[len ++] = (element->value.uint >> 8) & 0xff; + data[len ++] = (element->value.uint >> 0) & 0xff; + memcpy(data + len, bt_base_uuid, 12); + + return len + 12; + } + + data[0] = type | SDP_DSIZE_NEXT1; + if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { + if (element->type & SDP_DSIZE_MASK) + for (len = 0; element->value.str[len] | + element->value.str[len + 1]; len ++); + else + len = strlen(element->value.str); + memcpy(data + 2, element->value.str, data[1] = len); + + return len + 2; + } + + len = 2; + element = element->value.list; + while (element->type) + len += sdp_attr_write(data + len, element ++, uuid); + data[1] = len - 2; + + return len; +} + +static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a, + const struct sdp_service_attribute_s *b) +{ + return (int) b->attribute_id - a->attribute_id; +} + +static int sdp_uuid_compare(const int *a, const int *b) +{ + return *a - *b; +} + +static void sdp_service_record_build(struct sdp_service_record_s *record, + struct sdp_def_service_s *def, int handle) +{ + int len = 0; + uint8_t *data; + int *uuid; + + record->uuids = 0; + while (def->attributes[record->attributes].data.type) { + len += 3; + len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, + &record->uuids); + } + record->uuids = 1 << ffs(record->uuids - 1); + record->attribute_list = + qemu_mallocz(record->attributes * sizeof(*record->attribute_list)); + record->uuid = + qemu_mallocz(record->uuids * sizeof(*record->uuid)); + data = qemu_malloc(len); + + record->attributes = 0; + uuid = record->uuid; + while (def->attributes[record->attributes].data.type) { + record->attribute_list[record->attributes].pair = data; + + len = 0; + data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2; + data[len ++] = def->attributes[record->attributes].id >> 8; + data[len ++] = def->attributes[record->attributes].id & 0xff; + len += sdp_attr_write(data + len, + &def->attributes[record->attributes].data, &uuid); + + /* Special case: assign a ServiceRecordHandle in sequence */ + if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE) + def->attributes[record->attributes].data.value.uint = handle; + /* Note: we could also assign a ServiceDescription based on + * sdp->device.device->lmp_name. */ + + record->attribute_list[record->attributes ++].len = len; + data += len; + } + + /* Sort the attribute list by the AttributeID */ + qsort(record->attribute_list, record->attributes, + sizeof(*record->attribute_list), + (void *) sdp_attributeid_compare); + /* Sort the searchable UUIDs list for bisection */ + qsort(record->uuid, record->uuids, + sizeof(*record->uuid), + (void *) sdp_uuid_compare); +} + +static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp, + struct sdp_def_service_s **service) +{ + sdp->services = 0; + while (service[sdp->services]) + sdp->services ++; + sdp->service_list = + qemu_mallocz(sdp->services * sizeof(*sdp->service_list)); + + sdp->services = 0; + while (*service) { + sdp_service_record_build(&sdp->service_list[sdp->services], + *service, sdp->services); + service ++; + sdp->services ++; + } +} + +#define LAST { .type = 0 } +#define SERVICE(name, attrs) \ + static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \ + .attributes = { attrs { .data = LAST } }, \ + }; +#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val }, +#define UINT8(val) { \ + .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \ + .value.uint = val, \ + }, +#define UINT16(val) { \ + .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \ + .value.uint = val, \ + }, +#define UINT32(val) { \ + .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \ + .value.uint = val, \ + }, +#define UUID128(val) { \ + .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ + .value.uint = val, \ + }, +#define TRUE { \ + .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ + .value.uint = 1, \ + }, +#define FALSE { \ + .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ + .value.uint = 0, \ + }, +#define STRING(val) { \ + .type = SDP_DTYPE_STRING, \ + .value.str = val, \ + }, +#define ARRAY(...) { \ + .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \ + .value.str = (char []) { __VA_ARGS__, 0, 0 }, \ + }, +#define URL(val) { \ + .type = SDP_DTYPE_URL, \ + .value.str = val, \ + }, +#if 1 +#define LIST(val) { \ + .type = SDP_DTYPE_SEQ, \ + .value.list = (struct sdp_def_data_element_s []) { val LAST }, \ + }, +#endif + +/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes + * in resulting SDP data representation size. */ + +SERVICE(hid, + ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ + ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID))) + ATTRIBUTE(RECORD_STATE, UINT32(1)) + ATTRIBUTE(PROTO_DESC_LIST, LIST( + LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL)) + LIST(UUID128(HIDP_UUID)) + )) + ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) + ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( + UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) + )) + ATTRIBUTE(PFILE_DESC_LIST, LIST( + LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100)) + )) + ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) + ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID")) + ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse")) + ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) + + /* Profile specific */ + ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */ + ATTRIBUTE(PARSER_VERSION, UINT16(0x0111)) + /* TODO: extract from l2cap_device->device.class[0] */ + ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) + ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) + ATTRIBUTE(VIRTUAL_CABLE, TRUE) + ATTRIBUTE(RECONNECT_INITIATE, FALSE) + /* TODO: extract from hid->usbdev->report_desc */ + ATTRIBUTE(DESCRIPTOR_LIST, LIST( + LIST(UINT8(0x22) ARRAY( + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x06, /* Usage (Keyboard) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x08, /* Report Count (8) */ + 0x05, 0x07, /* Usage Page (Key Codes) */ + 0x19, 0xe0, /* Usage Minimum (224) */ + 0x29, 0xe7, /* Usage Maximum (231) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x08, /* Report Size (8) */ + 0x81, 0x01, /* Input (Constant) */ + 0x95, 0x05, /* Report Count (5) */ + 0x75, 0x01, /* Report Size (1) */ + 0x05, 0x08, /* Usage Page (LEDs) */ + 0x19, 0x01, /* Usage Minimum (1) */ + 0x29, 0x05, /* Usage Maximum (5) */ + 0x91, 0x02, /* Output (Data, Variable, Absolute) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x03, /* Report Size (3) */ + 0x91, 0x01, /* Output (Constant) */ + 0x95, 0x06, /* Report Count (6) */ + 0x75, 0x08, /* Report Size (8) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0xff, /* Logical Maximum (255) */ + 0x05, 0x07, /* Usage Page (Key Codes) */ + 0x19, 0x00, /* Usage Minimum (0) */ + 0x29, 0xff, /* Usage Maximum (255) */ + 0x81, 0x00, /* Input (Data, Array) */ + 0xc0 /* End Collection */ + )))) + ATTRIBUTE(LANG_ID_BASE_LIST, LIST( + LIST(UINT16(0x0409) UINT16(0x0100)) + )) + ATTRIBUTE(SDP_DISABLE, FALSE) + ATTRIBUTE(BATTERY_POWER, TRUE) + ATTRIBUTE(REMOTE_WAKEUP, TRUE) + ATTRIBUTE(BOOT_DEVICE, TRUE) /* XXX: untested */ + ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) + ATTRIBUTE(NORMALLY_CONNECTABLE, TRUE) + ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) +) + +SERVICE(sdp, + ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ + ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID))) + ATTRIBUTE(RECORD_STATE, UINT32(1)) + ATTRIBUTE(PROTO_DESC_LIST, LIST( + LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) + LIST(UUID128(SDP_UUID)) + )) + ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) + ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( + UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) + )) + ATTRIBUTE(PFILE_DESC_LIST, LIST( + LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100)) + )) + ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) + ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) + + /* Profile specific */ + ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100))) + ATTRIBUTE(SVCDB_STATE , UINT32(1)) +) + +SERVICE(pnp, + ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ + ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID))) + ATTRIBUTE(RECORD_STATE, UINT32(1)) + ATTRIBUTE(PROTO_DESC_LIST, LIST( + LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) + LIST(UUID128(SDP_UUID)) + )) + ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) + ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( + UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) + )) + ATTRIBUTE(PFILE_DESC_LIST, LIST( + LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100)) + )) + ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) + ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) + + /* Profile specific */ + ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) + ATTRIBUTE(VERSION, UINT16(0x0100)) + ATTRIBUTE(PRIMARY_RECORD, TRUE) +) + +static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params) +{ + struct bt_l2cap_sdp_state_s *sdp = qemu_mallocz(sizeof(*sdp)); + struct sdp_def_service_s *services[] = { + &sdp_service_sdp_s, + &sdp_service_hid_s, + &sdp_service_pnp_s, + 0, + }; + + sdp->channel = params; + sdp->channel->opaque = sdp; + sdp->channel->close = bt_l2cap_sdp_close_ch; + sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in; + + sdp_service_db_build(sdp, services); + + return 0; +} + +void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev) +{ + bt_l2cap_psm_register(dev, BT_PSM_SDP, + MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch); +} diff -Nur kvm-76/qemu/hw/bt.c kvm-userspace/qemu/hw/bt.c --- kvm-76/qemu/hw/bt.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt.c 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,123 @@ +/* + * Convenience functions for bluetooth. + * + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "net.h" +#include "bt.h" + +/* Slave implementations can ignore this */ +static void bt_dummy_lmp_mode_change(struct bt_link_s *link) +{ +} + +/* Slaves should never receive these PDUs */ +static void bt_dummy_lmp_connection_complete(struct bt_link_s *link) +{ + if (link->slave->reject_reason) + fprintf(stderr, "%s: stray LMP_not_accepted received, fixme\n", + __FUNCTION__); + else + fprintf(stderr, "%s: stray LMP_accepted received, fixme\n", + __FUNCTION__); + exit(-1); +} + +static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link) +{ + fprintf(stderr, "%s: stray LMP_detach received, fixme\n", __FUNCTION__); + exit(-1); +} + +static void bt_dummy_lmp_acl_resp(struct bt_link_s *link, + const uint8_t *data, int start, int len) +{ + fprintf(stderr, "%s: stray ACL response PDU, fixme\n", __FUNCTION__); + exit(-1); +} + +/* Slaves that don't hold any additional per link state can use these */ +static void bt_dummy_lmp_connection_request(struct bt_link_s *req) +{ + struct bt_link_s *link = qemu_mallocz(sizeof(struct bt_link_s)); + + link->slave = req->slave; + link->host = req->host; + + req->host->reject_reason = 0; + req->host->lmp_connection_complete(link); +} + +static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link) +{ + qemu_free(link); +} + +static void bt_dummy_destroy(struct bt_device_s *device) +{ + bt_device_done(device); + qemu_free(device); +} + +static int bt_dev_idx = 0; + +void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net) +{ + memset(dev, 0, sizeof(*dev)); + dev->inquiry_scan = 1; + dev->page_scan = 1; + + dev->bd_addr.b[0] = bt_dev_idx & 0xff; + dev->bd_addr.b[1] = bt_dev_idx >> 8; + dev->bd_addr.b[2] = 0xd0; + dev->bd_addr.b[3] = 0xba; + dev->bd_addr.b[4] = 0xbe; + dev->bd_addr.b[5] = 0xba; + bt_dev_idx ++; + + /* Simple slave-only devices need to implement only .lmp_acl_data */ + dev->lmp_connection_complete = bt_dummy_lmp_connection_complete; + dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master; + dev->lmp_acl_resp = bt_dummy_lmp_acl_resp; + dev->lmp_mode_change = bt_dummy_lmp_mode_change; + dev->lmp_connection_request = bt_dummy_lmp_connection_request; + dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave; + + dev->handle_destroy = bt_dummy_destroy; + + dev->net = net; + dev->next = net->slave; + net->slave = dev; +} + +void bt_device_done(struct bt_device_s *dev) +{ + struct bt_device_s **p = &dev->net->slave; + + while (*p && *p != dev) + p = &(*p)->next; + if (*p != dev) { + fprintf(stderr, "%s: bad bt device \"%s\"\n", __FUNCTION__, + dev->lmp_name ?: "(null)"); + exit(-1); + } + + *p = dev->next; +} diff -Nur kvm-76/qemu/hw/bt.h kvm-userspace/qemu/hw/bt.h --- kvm-76/qemu/hw/bt.h 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/bt.h 2008-10-12 09:38:23.000000000 +0200 @@ -0,0 +1,2188 @@ +/* + * QEMU Bluetooth HCI helpers. + * + * Copyright (C) 2007 OpenMoko, Inc. + * Written by Andrzej Zaborowski + * + * Useful definitions taken from BlueZ project's headers. + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2006 Marcel Holtmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* BD Address */ +typedef struct { + uint8_t b[6]; +} __attribute__((packed)) bdaddr_t; + +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) +#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) + +/* Copy, swap, convert BD Address */ +static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) +{ + return memcmp(ba1, ba2, sizeof(bdaddr_t)); +} +static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) +{ + memcpy(dst, src, sizeof(bdaddr_t)); +} + +#define BAINIT(orig) { .b = { \ + (orig)->b[0], (orig)->b[1], (orig)->b[2], \ + (orig)->b[3], (orig)->b[4], (orig)->b[5], \ +}, } + +/* The twisted structures of a bluetooth environment */ +struct bt_device_s; +struct bt_scatternet_s; +struct bt_piconet_s; +struct bt_link_s; + +struct bt_scatternet_s { + struct bt_device_s *slave; +}; + +struct bt_link_s { + struct bt_device_s *slave, *host; + uint16_t handle; /* Master (host) side handle */ + uint16_t acl_interval; + enum { + acl_active, + acl_hold, + acl_sniff, + acl_parked, + } acl_mode; +}; + +struct bt_device_s { + int lt_addr; + bdaddr_t bd_addr; + int mtu; + int setup; + struct bt_scatternet_s *net; + + uint8_t key[16]; + int key_present; + uint8_t class[3]; + + uint8_t reject_reason; + + uint64_t lmp_caps; + const char *lmp_name; + void (*lmp_connection_request)(struct bt_link_s *link); + void (*lmp_connection_complete)(struct bt_link_s *link); + void (*lmp_disconnect_master)(struct bt_link_s *link); + void (*lmp_disconnect_slave)(struct bt_link_s *link); + void (*lmp_acl_data)(struct bt_link_s *link, const uint8_t *data, + int start, int len); + void (*lmp_acl_resp)(struct bt_link_s *link, const uint8_t *data, + int start, int len); + void (*lmp_mode_change)(struct bt_link_s *link); + + void (*handle_destroy)(struct bt_device_s *device); + struct bt_device_s *next; /* Next in the piconet/scatternet */ + + int inquiry_scan; + int page_scan; + + uint16_t clkoff; /* Note: Always little-endian */ +}; + +/* bt.c */ +void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net); +void bt_device_done(struct bt_device_s *dev); + +/* bt-hci.c */ +struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net); + +/* bt-host.c */ +struct HCIInfo *bt_host_hci(const char *id); + +/* bt-vhci.c */ +void bt_vhci_init(struct HCIInfo *info); + +/* bt-hci-csr.c */ +enum { + csrhci_pin_reset, + csrhci_pin_wakeup, + __csrhci_pins, +}; +qemu_irq *csrhci_pins_get(CharDriverState *chr); +CharDriverState *uart_hci_init(qemu_irq wakeup); + +/* bt-l2cap.c */ +struct bt_l2cap_device_s; +struct bt_l2cap_conn_params_s; +struct bt_l2cap_psm_s; +void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, + struct bt_scatternet_s *net); +void bt_l2cap_device_done(struct bt_l2cap_device_s *dev); +void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, + int min_mtu, int (*new_channel)(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params)); + +struct bt_l2cap_device_s { + struct bt_device_s device; + struct bt_l2cap_psm_s *first_psm; +}; + +struct bt_l2cap_conn_params_s { + /* Input */ + uint8_t *(*sdu_out)(struct bt_l2cap_conn_params_s *chan, int len); + void (*sdu_submit)(struct bt_l2cap_conn_params_s *chan); + int remote_mtu; + /* Output */ + void *opaque; + void (*sdu_in)(void *opaque, const uint8_t *data, int len); + void (*close)(void *opaque); +}; + +enum bt_l2cap_psm_predef { + BT_PSM_SDP = 0x0001, + BT_PSM_RFCOMM = 0x0003, + BT_PSM_TELEPHONY = 0x0005, + BT_PSM_TCS = 0x0007, + BT_PSM_BNEP = 0x000f, + BT_PSM_HID_CTRL = 0x0011, + BT_PSM_HID_INTR = 0x0013, + BT_PSM_UPNP = 0x0015, + BT_PSM_AVCTP = 0x0017, + BT_PSM_AVDTP = 0x0019, +}; + +/* bt-sdp.c */ +void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev); + +/* bt-hid.c */ +struct bt_device_s *bt_mouse_init(struct bt_scatternet_s *net); +struct bt_device_s *bt_tablet_init(struct bt_scatternet_s *net); +struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net); + +/* Link Management Protocol layer defines */ + +#define LLID_ACLU_CONT 0x1 +#define LLID_ACLU_START 0x2 +#define LLID_ACLC 0x3 + +enum lmp_pdu_type { + LMP_NAME_REQ = 0x0001, + LMP_NAME_RES = 0x0002, + LMP_ACCEPTED = 0x0003, + LMP_NOT_ACCEPTED = 0x0004, + LMP_CLKOFFSET_REQ = 0x0005, + LMP_CLKOFFSET_RES = 0x0006, + LMP_DETACH = 0x0007, + LMP_IN_RAND = 0x0008, + LMP_COMB_KEY = 0x0009, + LMP_UNIT_KEY = 0x000a, + LMP_AU_RAND = 0x000b, + LMP_SRES = 0x000c, + LMP_TEMP_RAND = 0x000d, + LMP_TEMP_KEY = 0x000e, + LMP_CRYPT_MODE_REQ = 0x000f, + LMP_CRYPT_KEY_SIZE_REQ = 0x0010, + LMP_START_ENCRYPT_REQ = 0x0011, + LMP_STOP_ENCRYPT_REQ = 0x0012, + LMP_SWITCH_REQ = 0x0013, + LMP_HOLD = 0x0014, + LMP_HOLD_REQ = 0x0015, + LMP_SNIFF_REQ = 0x0017, + LMP_UNSNIFF_REQ = 0x0018, + LMP_LMP_PARK_REQ = 0x0019, + LMP_SET_BCAST_SCAN_WND = 0x001b, + LMP_MODIFY_BEACON = 0x001c, + LMP_UNPARK_BD_ADDR_REQ = 0x001d, + LMP_UNPARK_PM_ADDR_REQ = 0x001e, + LMP_INCR_POWER_REQ = 0x001f, + LMP_DECR_POWER_REQ = 0x0020, + LMP_MAX_POWER = 0x0021, + LMP_MIN_POWER = 0x0022, + LMP_AUTO_RATE = 0x0023, + LMP_PREFERRED_RATE = 0x0024, + LMP_VERSION_REQ = 0x0025, + LMP_VERSION_RES = 0x0026, + LMP_FEATURES_REQ = 0x0027, + LMP_FEATURES_RES = 0x0028, + LMP_QUALITY_OF_SERVICE = 0x0029, + LMP_QOS_REQ = 0x002a, + LMP_RM_SCO_LINK_REQ = 0x002b, + LMP_SCO_LINK_REQ = 0x002c, + LMP_MAX_SLOT = 0x002d, + LMP_MAX_SLOT_REQ = 0x002e, + LMP_TIMING_ACCURACY_REQ = 0x002f, + LMP_TIMING_ACCURACY_RES = 0x0030, + LMP_SETUP_COMPLETE = 0x0031, + LMP_USE_SEMIPERM_KEY = 0x0032, + LMP_HOST_CONNECTION_REQ = 0x0033, + LMP_SLOT_OFFSET = 0x0034, + LMP_PAGE_MODE_REQ = 0x0035, + LMP_PAGE_SCAN_MODE_REQ = 0x0036, + LMP_SUPERVISION_TIMEOUT = 0x0037, + LMP_TEST_ACTIVATE = 0x0038, + LMP_TEST_CONTROL = 0x0039, + LMP_CRYPT_KEY_MASK_REQ = 0x003a, + LMP_CRYPT_KEY_MASK_RES = 0x003b, + LMP_SET_AFH = 0x003c, + LMP_ACCEPTED_EXT = 0x7f01, + LMP_NOT_ACCEPTED_EXT = 0x7f02, + LMP_FEATURES_REQ_EXT = 0x7f03, + LMP_FEATURES_RES_EXT = 0x7f04, + LMP_PACKET_TYPE_TBL_REQ = 0x7f0b, + LMP_ESCO_LINK_REQ = 0x7f0c, + LMP_RM_ESCO_LINK_REQ = 0x7f0d, + LMP_CHANNEL_CLASS_REQ = 0x7f10, + LMP_CHANNEL_CLASS = 0x7f11, +}; + +/* Host Controller Interface layer defines */ + +enum hci_packet_type { + HCI_COMMAND_PKT = 0x01, + HCI_ACLDATA_PKT = 0x02, + HCI_SCODATA_PKT = 0x03, + HCI_EVENT_PKT = 0x04, + HCI_VENDOR_PKT = 0xff, +}; + +enum bt_packet_type { + HCI_2DH1 = 1 << 1, + HCI_3DH1 = 1 << 2, + HCI_DM1 = 1 << 3, + HCI_DH1 = 1 << 4, + HCI_2DH3 = 1 << 8, + HCI_3DH3 = 1 << 9, + HCI_DM3 = 1 << 10, + HCI_DH3 = 1 << 11, + HCI_2DH5 = 1 << 12, + HCI_3DH5 = 1 << 13, + HCI_DM5 = 1 << 14, + HCI_DH5 = 1 << 15, +}; + +enum sco_packet_type { + HCI_HV1 = 1 << 5, + HCI_HV2 = 1 << 6, + HCI_HV3 = 1 << 7, +}; + +enum ev_packet_type { + HCI_EV3 = 1 << 3, + HCI_EV4 = 1 << 4, + HCI_EV5 = 1 << 5, + HCI_2EV3 = 1 << 6, + HCI_3EV3 = 1 << 7, + HCI_2EV5 = 1 << 8, + HCI_3EV5 = 1 << 9, +}; + +enum hci_error_code { + HCI_SUCCESS = 0x00, + HCI_UNKNOWN_COMMAND = 0x01, + HCI_NO_CONNECTION = 0x02, + HCI_HARDWARE_FAILURE = 0x03, + HCI_PAGE_TIMEOUT = 0x04, + HCI_AUTHENTICATION_FAILURE = 0x05, + HCI_PIN_OR_KEY_MISSING = 0x06, + HCI_MEMORY_FULL = 0x07, + HCI_CONNECTION_TIMEOUT = 0x08, + HCI_MAX_NUMBER_OF_CONNECTIONS = 0x09, + HCI_MAX_NUMBER_OF_SCO_CONNECTIONS = 0x0a, + HCI_ACL_CONNECTION_EXISTS = 0x0b, + HCI_COMMAND_DISALLOWED = 0x0c, + HCI_REJECTED_LIMITED_RESOURCES = 0x0d, + HCI_REJECTED_SECURITY = 0x0e, + HCI_REJECTED_PERSONAL = 0x0f, + HCI_HOST_TIMEOUT = 0x10, + HCI_UNSUPPORTED_FEATURE = 0x11, + HCI_INVALID_PARAMETERS = 0x12, + HCI_OE_USER_ENDED_CONNECTION = 0x13, + HCI_OE_LOW_RESOURCES = 0x14, + HCI_OE_POWER_OFF = 0x15, + HCI_CONNECTION_TERMINATED = 0x16, + HCI_REPEATED_ATTEMPTS = 0x17, + HCI_PAIRING_NOT_ALLOWED = 0x18, + HCI_UNKNOWN_LMP_PDU = 0x19, + HCI_UNSUPPORTED_REMOTE_FEATURE = 0x1a, + HCI_SCO_OFFSET_REJECTED = 0x1b, + HCI_SCO_INTERVAL_REJECTED = 0x1c, + HCI_AIR_MODE_REJECTED = 0x1d, + HCI_INVALID_LMP_PARAMETERS = 0x1e, + HCI_UNSPECIFIED_ERROR = 0x1f, + HCI_UNSUPPORTED_LMP_PARAMETER_VALUE = 0x20, + HCI_ROLE_CHANGE_NOT_ALLOWED = 0x21, + HCI_LMP_RESPONSE_TIMEOUT = 0x22, + HCI_LMP_ERROR_TRANSACTION_COLLISION = 0x23, + HCI_LMP_PDU_NOT_ALLOWED = 0x24, + HCI_ENCRYPTION_MODE_NOT_ACCEPTED = 0x25, + HCI_UNIT_LINK_KEY_USED = 0x26, + HCI_QOS_NOT_SUPPORTED = 0x27, + HCI_INSTANT_PASSED = 0x28, + HCI_PAIRING_NOT_SUPPORTED = 0x29, + HCI_TRANSACTION_COLLISION = 0x2a, + HCI_QOS_UNACCEPTABLE_PARAMETER = 0x2c, + HCI_QOS_REJECTED = 0x2d, + HCI_CLASSIFICATION_NOT_SUPPORTED = 0x2e, + HCI_INSUFFICIENT_SECURITY = 0x2f, + HCI_PARAMETER_OUT_OF_RANGE = 0x30, + HCI_ROLE_SWITCH_PENDING = 0x32, + HCI_SLOT_VIOLATION = 0x34, + HCI_ROLE_SWITCH_FAILED = 0x35, +}; + +enum acl_flag_bits { + ACL_CONT = 1 << 0, + ACL_START = 1 << 1, + ACL_ACTIVE_BCAST = 1 << 2, + ACL_PICO_BCAST = 1 << 3, +}; + +enum baseband_link_type { + SCO_LINK = 0x00, + ACL_LINK = 0x01, +}; + +enum lmp_feature_bits0 { + LMP_3SLOT = 1 << 0, + LMP_5SLOT = 1 << 1, + LMP_ENCRYPT = 1 << 2, + LMP_SOFFSET = 1 << 3, + LMP_TACCURACY = 1 << 4, + LMP_RSWITCH = 1 << 5, + LMP_HOLD_MODE = 1 << 6, + LMP_SNIFF_MODE = 1 << 7, +}; + +enum lmp_feature_bits1 { + LMP_PARK = 1 << 0, + LMP_RSSI = 1 << 1, + LMP_QUALITY = 1 << 2, + LMP_SCO = 1 << 3, + LMP_HV2 = 1 << 4, + LMP_HV3 = 1 << 5, + LMP_ULAW = 1 << 6, + LMP_ALAW = 1 << 7, +}; + +enum lmp_feature_bits2 { + LMP_CVSD = 1 << 0, + LMP_PSCHEME = 1 << 1, + LMP_PCONTROL = 1 << 2, + LMP_TRSP_SCO = 1 << 3, + LMP_BCAST_ENC = 1 << 7, +}; + +enum lmp_feature_bits3 { + LMP_EDR_ACL_2M = 1 << 1, + LMP_EDR_ACL_3M = 1 << 2, + LMP_ENH_ISCAN = 1 << 3, + LMP_ILACE_ISCAN = 1 << 4, + LMP_ILACE_PSCAN = 1 << 5, + LMP_RSSI_INQ = 1 << 6, + LMP_ESCO = 1 << 7, +}; + +enum lmp_feature_bits4 { + LMP_EV4 = 1 << 0, + LMP_EV5 = 1 << 1, + LMP_AFH_CAP_SLV = 1 << 3, + LMP_AFH_CLS_SLV = 1 << 4, + LMP_EDR_3SLOT = 1 << 7, +}; + +enum lmp_feature_bits5 { + LMP_EDR_5SLOT = 1 << 0, + LMP_SNIFF_SUBR = 1 << 1, + LMP_AFH_CAP_MST = 1 << 3, + LMP_AFH_CLS_MST = 1 << 4, + LMP_EDR_ESCO_2M = 1 << 5, + LMP_EDR_ESCO_3M = 1 << 6, + LMP_EDR_3S_ESCO = 1 << 7, +}; + +enum lmp_feature_bits6 { + LMP_EXT_INQ = 1 << 0, +}; + +enum lmp_feature_bits7 { + LMP_EXT_FEAT = 1 << 7, +}; + +enum hci_link_policy { + HCI_LP_RSWITCH = 1 << 0, + HCI_LP_HOLD = 1 << 1, + HCI_LP_SNIFF = 1 << 2, + HCI_LP_PARK = 1 << 3, +}; + +enum hci_link_mode { + HCI_LM_ACCEPT = 1 << 15, + HCI_LM_MASTER = 1 << 0, + HCI_LM_AUTH = 1 << 1, + HCI_LM_ENCRYPT = 1 << 2, + HCI_LM_TRUSTED = 1 << 3, + HCI_LM_RELIABLE = 1 << 4, + HCI_LM_SECURE = 1 << 5, +}; + +/* HCI Commands */ + +/* Link Control */ +#define OGF_LINK_CTL 0x01 + +#define OCF_INQUIRY 0x0001 +typedef struct { + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} __attribute__ ((packed)) inquiry_cp; +#define INQUIRY_CP_SIZE 5 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) status_bdaddr_rp; +#define STATUS_BDADDR_RP_SIZE 7 + +#define OCF_INQUIRY_CANCEL 0x0002 + +#define OCF_PERIODIC_INQUIRY 0x0003 +typedef struct { + uint16_t max_period; /* 1.28s units */ + uint16_t min_period; /* 1.28s units */ + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} __attribute__ ((packed)) periodic_inquiry_cp; +#define PERIODIC_INQUIRY_CP_SIZE 9 + +#define OCF_EXIT_PERIODIC_INQUIRY 0x0004 + +#define OCF_CREATE_CONN 0x0005 +typedef struct { + bdaddr_t bdaddr; + uint16_t pkt_type; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; + uint8_t role_switch; +} __attribute__ ((packed)) create_conn_cp; +#define CREATE_CONN_CP_SIZE 13 + +#define OCF_DISCONNECT 0x0006 +typedef struct { + uint16_t handle; + uint8_t reason; +} __attribute__ ((packed)) disconnect_cp; +#define DISCONNECT_CP_SIZE 3 + +#define OCF_ADD_SCO 0x0007 +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} __attribute__ ((packed)) add_sco_cp; +#define ADD_SCO_CP_SIZE 4 + +#define OCF_CREATE_CONN_CANCEL 0x0008 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) create_conn_cancel_cp; +#define CREATE_CONN_CANCEL_CP_SIZE 6 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) create_conn_cancel_rp; +#define CREATE_CONN_CANCEL_RP_SIZE 7 + +#define OCF_ACCEPT_CONN_REQ 0x0009 +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) accept_conn_req_cp; +#define ACCEPT_CONN_REQ_CP_SIZE 7 + +#define OCF_REJECT_CONN_REQ 0x000A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} __attribute__ ((packed)) reject_conn_req_cp; +#define REJECT_CONN_REQ_CP_SIZE 7 + +#define OCF_LINK_KEY_REPLY 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; +} __attribute__ ((packed)) link_key_reply_cp; +#define LINK_KEY_REPLY_CP_SIZE 22 + +#define OCF_LINK_KEY_NEG_REPLY 0x000C + +#define OCF_PIN_CODE_REPLY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t pin_len; + uint8_t pin_code[16]; +} __attribute__ ((packed)) pin_code_reply_cp; +#define PIN_CODE_REPLY_CP_SIZE 23 + +#define OCF_PIN_CODE_NEG_REPLY 0x000E + +#define OCF_SET_CONN_PTYPE 0x000F +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} __attribute__ ((packed)) set_conn_ptype_cp; +#define SET_CONN_PTYPE_CP_SIZE 4 + +#define OCF_AUTH_REQUESTED 0x0011 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) auth_requested_cp; +#define AUTH_REQUESTED_CP_SIZE 2 + +#define OCF_SET_CONN_ENCRYPT 0x0013 +typedef struct { + uint16_t handle; + uint8_t encrypt; +} __attribute__ ((packed)) set_conn_encrypt_cp; +#define SET_CONN_ENCRYPT_CP_SIZE 3 + +#define OCF_CHANGE_CONN_LINK_KEY 0x0015 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) change_conn_link_key_cp; +#define CHANGE_CONN_LINK_KEY_CP_SIZE 2 + +#define OCF_MASTER_LINK_KEY 0x0017 +typedef struct { + uint8_t key_flag; +} __attribute__ ((packed)) master_link_key_cp; +#define MASTER_LINK_KEY_CP_SIZE 1 + +#define OCF_REMOTE_NAME_REQ 0x0019 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; +} __attribute__ ((packed)) remote_name_req_cp; +#define REMOTE_NAME_REQ_CP_SIZE 10 + +#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) remote_name_req_cancel_cp; +#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) remote_name_req_cancel_rp; +#define REMOTE_NAME_REQ_CANCEL_RP_SIZE 7 + +#define OCF_READ_REMOTE_FEATURES 0x001B +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_remote_features_cp; +#define READ_REMOTE_FEATURES_CP_SIZE 2 + +#define OCF_READ_REMOTE_EXT_FEATURES 0x001C +typedef struct { + uint16_t handle; + uint8_t page_num; +} __attribute__ ((packed)) read_remote_ext_features_cp; +#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 + +#define OCF_READ_REMOTE_VERSION 0x001D +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_remote_version_cp; +#define READ_REMOTE_VERSION_CP_SIZE 2 + +#define OCF_READ_CLOCK_OFFSET 0x001F +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_clock_offset_cp; +#define READ_CLOCK_OFFSET_CP_SIZE 2 + +#define OCF_READ_LMP_HANDLE 0x0020 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_lmp_handle_cp; +#define READ_LMP_HANDLE_CP_SIZE 2 + +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t lmp_handle; + uint32_t reserved; +} __attribute__ ((packed)) read_lmp_handle_rp; +#define READ_LMP_HANDLE_RP_SIZE 8 + +#define OCF_SETUP_SYNC_CONN 0x0028 +typedef struct { + uint16_t handle; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} __attribute__ ((packed)) setup_sync_conn_cp; +#define SETUP_SYNC_CONN_CP_SIZE 17 + +#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 +typedef struct { + bdaddr_t bdaddr; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} __attribute__ ((packed)) accept_sync_conn_req_cp; +#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 + +#define OCF_REJECT_SYNC_CONN_REQ 0x002A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} __attribute__ ((packed)) reject_sync_conn_req_cp; +#define REJECT_SYNC_CONN_REQ_CP_SIZE 7 + +/* Link Policy */ +#define OGF_LINK_POLICY 0x02 + +#define OCF_HOLD_MODE 0x0001 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} __attribute__ ((packed)) hold_mode_cp; +#define HOLD_MODE_CP_SIZE 6 + +#define OCF_SNIFF_MODE 0x0003 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; + uint16_t attempt; + uint16_t timeout; +} __attribute__ ((packed)) sniff_mode_cp; +#define SNIFF_MODE_CP_SIZE 10 + +#define OCF_EXIT_SNIFF_MODE 0x0004 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) exit_sniff_mode_cp; +#define EXIT_SNIFF_MODE_CP_SIZE 2 + +#define OCF_PARK_MODE 0x0005 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} __attribute__ ((packed)) park_mode_cp; +#define PARK_MODE_CP_SIZE 6 + +#define OCF_EXIT_PARK_MODE 0x0006 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) exit_park_mode_cp; +#define EXIT_PARK_MODE_CP_SIZE 2 + +#define OCF_QOS_SETUP 0x0007 +typedef struct { + uint8_t service_type; /* 1 = best effort */ + uint32_t token_rate; /* Byte per seconds */ + uint32_t peak_bandwidth; /* Byte per seconds */ + uint32_t latency; /* Microseconds */ + uint32_t delay_variation; /* Microseconds */ +} __attribute__ ((packed)) hci_qos; +#define HCI_QOS_CP_SIZE 17 +typedef struct { + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} __attribute__ ((packed)) qos_setup_cp; +#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) + +#define OCF_ROLE_DISCOVERY 0x0009 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) role_discovery_cp; +#define ROLE_DISCOVERY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t role; +} __attribute__ ((packed)) role_discovery_rp; +#define ROLE_DISCOVERY_RP_SIZE 4 + +#define OCF_SWITCH_ROLE 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) switch_role_cp; +#define SWITCH_ROLE_CP_SIZE 7 + +#define OCF_READ_LINK_POLICY 0x000C +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_link_policy_cp; +#define READ_LINK_POLICY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t policy; +} __attribute__ ((packed)) read_link_policy_rp; +#define READ_LINK_POLICY_RP_SIZE 5 + +#define OCF_WRITE_LINK_POLICY 0x000D +typedef struct { + uint16_t handle; + uint16_t policy; +} __attribute__ ((packed)) write_link_policy_cp; +#define WRITE_LINK_POLICY_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) write_link_policy_rp; +#define WRITE_LINK_POLICY_RP_SIZE 3 + +#define OCF_READ_DEFAULT_LINK_POLICY 0x000E + +#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F + +#define OCF_FLOW_SPECIFICATION 0x0010 + +#define OCF_SNIFF_SUBRATE 0x0011 +typedef struct { + uint16_t handle; + uint16_t max_remote_latency; + uint16_t max_local_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} __attribute__ ((packed)) sniff_subrate_cp; +#define SNIFF_SUBRATE_CP_SIZE 10 + +/* Host Controller and Baseband */ +#define OGF_HOST_CTL 0x03 + +#define OCF_SET_EVENT_MASK 0x0001 +typedef struct { + uint8_t mask[8]; +} __attribute__ ((packed)) set_event_mask_cp; +#define SET_EVENT_MASK_CP_SIZE 8 + +#define OCF_RESET 0x0003 + +#define OCF_SET_EVENT_FLT 0x0005 +typedef struct { + uint8_t flt_type; + uint8_t cond_type; + uint8_t condition[0]; +} __attribute__ ((packed)) set_event_flt_cp; +#define SET_EVENT_FLT_CP_SIZE 2 + +enum bt_filter_type { + FLT_CLEAR_ALL = 0x00, + FLT_INQ_RESULT = 0x01, + FLT_CONN_SETUP = 0x02, +}; +enum inq_result_cond_type { + INQ_RESULT_RETURN_ALL = 0x00, + INQ_RESULT_RETURN_CLASS = 0x01, + INQ_RESULT_RETURN_BDADDR = 0x02, +}; +enum conn_setup_cond_type { + CONN_SETUP_ALLOW_ALL = 0x00, + CONN_SETUP_ALLOW_CLASS = 0x01, + CONN_SETUP_ALLOW_BDADDR = 0x02, +}; +enum conn_setup_cond { + CONN_SETUP_AUTO_OFF = 0x01, + CONN_SETUP_AUTO_ON = 0x02, +}; + +#define OCF_FLUSH 0x0008 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) flush_cp; +#define FLUSH_CP_SIZE 2 + +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) flush_rp; +#define FLUSH_RP_SIZE 3 + +#define OCF_READ_PIN_TYPE 0x0009 +typedef struct { + uint8_t status; + uint8_t pin_type; +} __attribute__ ((packed)) read_pin_type_rp; +#define READ_PIN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_PIN_TYPE 0x000A +typedef struct { + uint8_t pin_type; +} __attribute__ ((packed)) write_pin_type_cp; +#define WRITE_PIN_TYPE_CP_SIZE 1 + +#define OCF_CREATE_NEW_UNIT_KEY 0x000B + +#define OCF_READ_STORED_LINK_KEY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t read_all; +} __attribute__ ((packed)) read_stored_link_key_cp; +#define READ_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t max_keys; + uint16_t num_keys; +} __attribute__ ((packed)) read_stored_link_key_rp; +#define READ_STORED_LINK_KEY_RP_SIZE 5 + +#define OCF_WRITE_STORED_LINK_KEY 0x0011 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} __attribute__ ((packed)) write_stored_link_key_cp; +#define WRITE_STORED_LINK_KEY_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t num_keys; +} __attribute__ ((packed)) write_stored_link_key_rp; +#define READ_WRITE_LINK_KEY_RP_SIZE 2 + +#define OCF_DELETE_STORED_LINK_KEY 0x0012 +typedef struct { + bdaddr_t bdaddr; + uint8_t delete_all; +} __attribute__ ((packed)) delete_stored_link_key_cp; +#define DELETE_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t num_keys; +} __attribute__ ((packed)) delete_stored_link_key_rp; +#define DELETE_STORED_LINK_KEY_RP_SIZE 3 + +#define OCF_CHANGE_LOCAL_NAME 0x0013 +typedef struct { + char name[248]; +} __attribute__ ((packed)) change_local_name_cp; +#define CHANGE_LOCAL_NAME_CP_SIZE 248 + +#define OCF_READ_LOCAL_NAME 0x0014 +typedef struct { + uint8_t status; + char name[248]; +} __attribute__ ((packed)) read_local_name_rp; +#define READ_LOCAL_NAME_RP_SIZE 249 + +#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 +typedef struct { + uint8_t status; + uint16_t timeout; +} __attribute__ ((packed)) read_conn_accept_timeout_rp; +#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 +typedef struct { + uint16_t timeout; +} __attribute__ ((packed)) write_conn_accept_timeout_cp; +#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_PAGE_TIMEOUT 0x0017 +typedef struct { + uint8_t status; + uint16_t timeout; +} __attribute__ ((packed)) read_page_timeout_rp; +#define READ_PAGE_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_PAGE_TIMEOUT 0x0018 +typedef struct { + uint16_t timeout; +} __attribute__ ((packed)) write_page_timeout_cp; +#define WRITE_PAGE_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_SCAN_ENABLE 0x0019 +typedef struct { + uint8_t status; + uint8_t enable; +} __attribute__ ((packed)) read_scan_enable_rp; +#define READ_SCAN_ENABLE_RP_SIZE 2 + +#define OCF_WRITE_SCAN_ENABLE 0x001A +typedef struct { + uint8_t scan_enable; +} __attribute__ ((packed)) write_scan_enable_cp; +#define WRITE_SCAN_ENABLE_CP_SIZE 1 + +enum scan_enable_bits { + SCAN_DISABLED = 0, + SCAN_INQUIRY = 1 << 0, + SCAN_PAGE = 1 << 1, +}; + +#define OCF_READ_PAGE_ACTIVITY 0x001B +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) read_page_activity_rp; +#define READ_PAGE_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_PAGE_ACTIVITY 0x001C +typedef struct { + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) write_page_activity_cp; +#define WRITE_PAGE_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_INQ_ACTIVITY 0x001D +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) read_inq_activity_rp; +#define READ_INQ_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_INQ_ACTIVITY 0x001E +typedef struct { + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) write_inq_activity_cp; +#define WRITE_INQ_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_AUTH_ENABLE 0x001F + +#define OCF_WRITE_AUTH_ENABLE 0x0020 + +#define AUTH_DISABLED 0x00 +#define AUTH_ENABLED 0x01 + +#define OCF_READ_ENCRYPT_MODE 0x0021 + +#define OCF_WRITE_ENCRYPT_MODE 0x0022 + +#define ENCRYPT_DISABLED 0x00 +#define ENCRYPT_P2P 0x01 +#define ENCRYPT_BOTH 0x02 + +#define OCF_READ_CLASS_OF_DEV 0x0023 +typedef struct { + uint8_t status; + uint8_t dev_class[3]; +} __attribute__ ((packed)) read_class_of_dev_rp; +#define READ_CLASS_OF_DEV_RP_SIZE 4 + +#define OCF_WRITE_CLASS_OF_DEV 0x0024 +typedef struct { + uint8_t dev_class[3]; +} __attribute__ ((packed)) write_class_of_dev_cp; +#define WRITE_CLASS_OF_DEV_CP_SIZE 3 + +#define OCF_READ_VOICE_SETTING 0x0025 +typedef struct { + uint8_t status; + uint16_t voice_setting; +} __attribute__ ((packed)) read_voice_setting_rp; +#define READ_VOICE_SETTING_RP_SIZE 3 + +#define OCF_WRITE_VOICE_SETTING 0x0026 +typedef struct { + uint16_t voice_setting; +} __attribute__ ((packed)) write_voice_setting_cp; +#define WRITE_VOICE_SETTING_CP_SIZE 2 + +#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 + +#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 + +#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 + +#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A + +#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B + +#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C + +#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D +typedef struct { + uint16_t handle; + uint8_t type; +} __attribute__ ((packed)) read_transmit_power_level_cp; +#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t level; +} __attribute__ ((packed)) read_transmit_power_level_rp; +#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 + +#define OCF_HOST_BUFFER_SIZE 0x0033 +typedef struct { + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} __attribute__ ((packed)) host_buffer_size_cp; +#define HOST_BUFFER_SIZE_CP_SIZE 7 + +#define OCF_HOST_NUMBER_OF_COMPLETED_PACKETS 0x0035 + +#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t link_sup_to; +} __attribute__ ((packed)) read_link_supervision_timeout_rp; +#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 + +#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 +typedef struct { + uint16_t handle; + uint16_t link_sup_to; +} __attribute__ ((packed)) write_link_supervision_timeout_cp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) write_link_supervision_timeout_rp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 + +#define OCF_READ_NUM_SUPPORTED_IAC 0x0038 + +#define MAX_IAC_LAP 0x40 +#define OCF_READ_CURRENT_IAC_LAP 0x0039 +typedef struct { + uint8_t status; + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} __attribute__ ((packed)) read_current_iac_lap_rp; +#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP + +#define OCF_WRITE_CURRENT_IAC_LAP 0x003A +typedef struct { + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} __attribute__ ((packed)) write_current_iac_lap_cp; +#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP + +#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B + +#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C + +#define OCF_READ_PAGE_SCAN_MODE 0x003D + +#define OCF_WRITE_PAGE_SCAN_MODE 0x003E + +#define OCF_SET_AFH_CLASSIFICATION 0x003F +typedef struct { + uint8_t map[10]; +} __attribute__ ((packed)) set_afh_classification_cp; +#define SET_AFH_CLASSIFICATION_CP_SIZE 10 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) set_afh_classification_rp; +#define SET_AFH_CLASSIFICATION_RP_SIZE 1 + +#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 +typedef struct { + uint8_t status; + uint8_t type; +} __attribute__ ((packed)) read_inquiry_scan_type_rp; +#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 +typedef struct { + uint8_t type; +} __attribute__ ((packed)) write_inquiry_scan_type_cp; +#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_inquiry_scan_type_rp; +#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 + +#define OCF_READ_INQUIRY_MODE 0x0044 +typedef struct { + uint8_t status; + uint8_t mode; +} __attribute__ ((packed)) read_inquiry_mode_rp; +#define READ_INQUIRY_MODE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_MODE 0x0045 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_inquiry_mode_cp; +#define WRITE_INQUIRY_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_inquiry_mode_rp; +#define WRITE_INQUIRY_MODE_RP_SIZE 1 + +#define OCF_READ_PAGE_SCAN_TYPE 0x0046 + +#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 + +#define OCF_READ_AFH_MODE 0x0048 +typedef struct { + uint8_t status; + uint8_t mode; +} __attribute__ ((packed)) read_afh_mode_rp; +#define READ_AFH_MODE_RP_SIZE 2 + +#define OCF_WRITE_AFH_MODE 0x0049 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_afh_mode_cp; +#define WRITE_AFH_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_afh_mode_rp; +#define WRITE_AFH_MODE_RP_SIZE 1 + +#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 +typedef struct { + uint8_t status; + uint8_t fec; + uint8_t data[240]; +} __attribute__ ((packed)) read_ext_inquiry_response_rp; +#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 + +#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 +typedef struct { + uint8_t fec; + uint8_t data[240]; +} __attribute__ ((packed)) write_ext_inquiry_response_cp; +#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_ext_inquiry_response_rp; +#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 + +/* Informational Parameters */ +#define OGF_INFO_PARAM 0x04 + +#define OCF_READ_LOCAL_VERSION 0x0001 +typedef struct { + uint8_t status; + uint8_t hci_ver; + uint16_t hci_rev; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} __attribute__ ((packed)) read_local_version_rp; +#define READ_LOCAL_VERSION_RP_SIZE 9 + +#define OCF_READ_LOCAL_COMMANDS 0x0002 +typedef struct { + uint8_t status; + uint8_t commands[64]; +} __attribute__ ((packed)) read_local_commands_rp; +#define READ_LOCAL_COMMANDS_RP_SIZE 65 + +#define OCF_READ_LOCAL_FEATURES 0x0003 +typedef struct { + uint8_t status; + uint8_t features[8]; +} __attribute__ ((packed)) read_local_features_rp; +#define READ_LOCAL_FEATURES_RP_SIZE 9 + +#define OCF_READ_LOCAL_EXT_FEATURES 0x0004 +typedef struct { + uint8_t page_num; +} __attribute__ ((packed)) read_local_ext_features_cp; +#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} __attribute__ ((packed)) read_local_ext_features_rp; +#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 + +#define OCF_READ_BUFFER_SIZE 0x0005 +typedef struct { + uint8_t status; + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} __attribute__ ((packed)) read_buffer_size_rp; +#define READ_BUFFER_SIZE_RP_SIZE 8 + +#define OCF_READ_COUNTRY_CODE 0x0007 +typedef struct { + uint8_t status; + uint8_t country_code; +} __attribute__ ((packed)) read_country_code_rp; +#define READ_COUNTRY_CODE_RP_SIZE 2 + +#define OCF_READ_BD_ADDR 0x0009 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) read_bd_addr_rp; +#define READ_BD_ADDR_RP_SIZE 7 + +/* Status params */ +#define OGF_STATUS_PARAM 0x05 + +#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t counter; +} __attribute__ ((packed)) read_failed_contact_counter_rp; +#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 + +#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) reset_failed_contact_counter_rp; +#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4 + +#define OCF_READ_LINK_QUALITY 0x0003 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_link_quality_cp; +#define READ_LINK_QUALITY_CP_SIZE 4 + +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t link_quality; +} __attribute__ ((packed)) read_link_quality_rp; +#define READ_LINK_QUALITY_RP_SIZE 4 + +#define OCF_READ_RSSI 0x0005 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t rssi; +} __attribute__ ((packed)) read_rssi_rp; +#define READ_RSSI_RP_SIZE 4 + +#define OCF_READ_AFH_MAP 0x0006 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint8_t map[10]; +} __attribute__ ((packed)) read_afh_map_rp; +#define READ_AFH_MAP_RP_SIZE 14 + +#define OCF_READ_CLOCK 0x0007 +typedef struct { + uint16_t handle; + uint8_t which_clock; +} __attribute__ ((packed)) read_clock_cp; +#define READ_CLOCK_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + uint32_t clock; + uint16_t accuracy; +} __attribute__ ((packed)) read_clock_rp; +#define READ_CLOCK_RP_SIZE 9 + +/* Testing commands */ +#define OGF_TESTING_CMD 0x3e + +/* Vendor specific commands */ +#define OGF_VENDOR_CMD 0x3f + +/* HCI Events */ + +#define EVT_INQUIRY_COMPLETE 0x01 + +#define EVT_INQUIRY_RESULT 0x02 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; +} __attribute__ ((packed)) inquiry_info; +#define INQUIRY_INFO_SIZE 14 + +#define EVT_CONN_COMPLETE 0x03 +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t encr_mode; +} __attribute__ ((packed)) evt_conn_complete; +#define EVT_CONN_COMPLETE_SIZE 11 + +#define EVT_CONN_REQUEST 0x04 +typedef struct { + bdaddr_t bdaddr; + uint8_t dev_class[3]; + uint8_t link_type; +} __attribute__ ((packed)) evt_conn_request; +#define EVT_CONN_REQUEST_SIZE 10 + +#define EVT_DISCONN_COMPLETE 0x05 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t reason; +} __attribute__ ((packed)) evt_disconn_complete; +#define EVT_DISCONN_COMPLETE_SIZE 4 + +#define EVT_AUTH_COMPLETE 0x06 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_auth_complete; +#define EVT_AUTH_COMPLETE_SIZE 3 + +#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + char name[248]; +} __attribute__ ((packed)) evt_remote_name_req_complete; +#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 + +#define EVT_ENCRYPT_CHANGE 0x08 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t encrypt; +} __attribute__ ((packed)) evt_encrypt_change; +#define EVT_ENCRYPT_CHANGE_SIZE 5 + +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_change_conn_link_key_complete; +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 + +#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t key_flag; +} __attribute__ ((packed)) evt_master_link_key_complete; +#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 + +#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t features[8]; +} __attribute__ ((packed)) evt_read_remote_features_complete; +#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 + +#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} __attribute__ ((packed)) evt_read_remote_version_complete; +#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 + +#define EVT_QOS_SETUP_COMPLETE 0x0D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} __attribute__ ((packed)) evt_qos_setup_complete; +#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) + +#define EVT_CMD_COMPLETE 0x0E +typedef struct { + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_complete; +#define EVT_CMD_COMPLETE_SIZE 3 + +#define EVT_CMD_STATUS 0x0F +typedef struct { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_status; +#define EVT_CMD_STATUS_SIZE 4 + +#define EVT_HARDWARE_ERROR 0x10 +typedef struct { + uint8_t code; +} __attribute__ ((packed)) evt_hardware_error; +#define EVT_HARDWARE_ERROR_SIZE 1 + +#define EVT_FLUSH_OCCURRED 0x11 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) evt_flush_occured; +#define EVT_FLUSH_OCCURRED_SIZE 2 + +#define EVT_ROLE_CHANGE 0x12 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) evt_role_change; +#define EVT_ROLE_CHANGE_SIZE 8 + +#define EVT_NUM_COMP_PKTS 0x13 +typedef struct { + uint8_t num_hndl; + struct { + uint16_t handle; + uint16_t num_packets; + } connection[0]; +} __attribute__ ((packed)) evt_num_comp_pkts; +#define EVT_NUM_COMP_PKTS_SIZE(num_hndl) (1 + 4 * (num_hndl)) + +#define EVT_MODE_CHANGE 0x14 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint16_t interval; +} __attribute__ ((packed)) evt_mode_change; +#define EVT_MODE_CHANGE_SIZE 6 + +#define EVT_RETURN_LINK_KEYS 0x15 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} __attribute__ ((packed)) evt_return_link_keys; +#define EVT_RETURN_LINK_KEYS_SIZE 1 + +#define EVT_PIN_CODE_REQ 0x16 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_pin_code_req; +#define EVT_PIN_CODE_REQ_SIZE 6 + +#define EVT_LINK_KEY_REQ 0x17 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_link_key_req; +#define EVT_LINK_KEY_REQ_SIZE 6 + +#define EVT_LINK_KEY_NOTIFY 0x18 +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; + uint8_t key_type; +} __attribute__ ((packed)) evt_link_key_notify; +#define EVT_LINK_KEY_NOTIFY_SIZE 23 + +#define EVT_LOOPBACK_COMMAND 0x19 + +#define EVT_DATA_BUFFER_OVERFLOW 0x1A +typedef struct { + uint8_t link_type; +} __attribute__ ((packed)) evt_data_buffer_overflow; +#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 + +#define EVT_MAX_SLOTS_CHANGE 0x1B +typedef struct { + uint16_t handle; + uint8_t max_slots; +} __attribute__ ((packed)) evt_max_slots_change; +#define EVT_MAX_SLOTS_CHANGE_SIZE 3 + +#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t clock_offset; +} __attribute__ ((packed)) evt_read_clock_offset_complete; +#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 + +#define EVT_CONN_PTYPE_CHANGED 0x1D +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t ptype; +} __attribute__ ((packed)) evt_conn_ptype_changed; +#define EVT_CONN_PTYPE_CHANGED_SIZE 5 + +#define EVT_QOS_VIOLATION 0x1E +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) evt_qos_violation; +#define EVT_QOS_VIOLATION_SIZE 2 + +#define EVT_PSCAN_REP_MODE_CHANGE 0x20 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; +} __attribute__ ((packed)) evt_pscan_rep_mode_change; +#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 + +#define EVT_FLOW_SPEC_COMPLETE 0x21 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; + uint8_t direction; + hci_qos qos; +} __attribute__ ((packed)) evt_flow_spec_complete; +#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) + +#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} __attribute__ ((packed)) inquiry_info_with_rssi; +#define INQUIRY_INFO_WITH_RSSI_SIZE 15 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode; +#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 16 + +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} __attribute__ ((packed)) evt_read_remote_ext_features_complete; +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 + +#define EVT_SYNC_CONN_COMPLETE 0x2C +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; + uint8_t air_mode; +} __attribute__ ((packed)) evt_sync_conn_complete; +#define EVT_SYNC_CONN_COMPLETE_SIZE 17 + +#define EVT_SYNC_CONN_CHANGED 0x2D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; +} __attribute__ ((packed)) evt_sync_conn_changed; +#define EVT_SYNC_CONN_CHANGED_SIZE 9 + +#define EVT_SNIFF_SUBRATE 0x2E +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t max_remote_latency; + uint16_t max_local_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} __attribute__ ((packed)) evt_sniff_subrate; +#define EVT_SNIFF_SUBRATE_SIZE 11 + +#define EVT_EXTENDED_INQUIRY_RESULT 0x2F +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; + uint8_t data[240]; +} __attribute__ ((packed)) extended_inquiry_info; +#define EXTENDED_INQUIRY_INFO_SIZE 254 + +#define EVT_TESTING 0xFE + +#define EVT_VENDOR 0xFF + +/* Command opcode pack/unpack */ +#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) +#define cmd_opcode_ogf(op) (op >> 10) +#define cmd_opcode_ocf(op) (op & 0x03ff) + +/* ACL handle and flags pack/unpack */ +#define acl_handle_pack(h, f) (uint16_t)(((h) & 0x0fff)|((f) << 12)) +#define acl_handle(h) ((h) & 0x0fff) +#define acl_flags(h) ((h) >> 12) + +/* HCI Packet structures */ +#define HCI_COMMAND_HDR_SIZE 3 +#define HCI_EVENT_HDR_SIZE 2 +#define HCI_ACL_HDR_SIZE 4 +#define HCI_SCO_HDR_SIZE 3 + +struct hci_command_hdr { + uint16_t opcode; /* OCF & OGF */ + uint8_t plen; +} __attribute__ ((packed)); + +struct hci_event_hdr { + uint8_t evt; + uint8_t plen; +} __attribute__ ((packed)); + +struct hci_acl_hdr { + uint16_t handle; /* Handle & Flags(PB, BC) */ + uint16_t dlen; +} __attribute__ ((packed)); + +struct hci_sco_hdr { + uint16_t handle; + uint8_t dlen; +} __attribute__ ((packed)); + +/* L2CAP layer defines */ + +enum bt_l2cap_lm_bits { + L2CAP_LM_MASTER = 1 << 0, + L2CAP_LM_AUTH = 1 << 1, + L2CAP_LM_ENCRYPT = 1 << 2, + L2CAP_LM_TRUSTED = 1 << 3, + L2CAP_LM_RELIABLE = 1 << 4, + L2CAP_LM_SECURE = 1 << 5, +}; + +enum bt_l2cap_cid_predef { + L2CAP_CID_INVALID = 0x0000, + L2CAP_CID_SIGNALLING= 0x0001, + L2CAP_CID_GROUP = 0x0002, + L2CAP_CID_ALLOC = 0x0040, +}; + +/* L2CAP command codes */ +enum bt_l2cap_cmd { + L2CAP_COMMAND_REJ = 1, + L2CAP_CONN_REQ, + L2CAP_CONN_RSP, + L2CAP_CONF_REQ, + L2CAP_CONF_RSP, + L2CAP_DISCONN_REQ, + L2CAP_DISCONN_RSP, + L2CAP_ECHO_REQ, + L2CAP_ECHO_RSP, + L2CAP_INFO_REQ, + L2CAP_INFO_RSP, +}; + +enum bt_l2cap_sar_bits { + L2CAP_SAR_NO_SEG = 0, + L2CAP_SAR_START, + L2CAP_SAR_END, + L2CAP_SAR_CONT, +}; + +/* L2CAP structures */ +typedef struct { + uint16_t len; + uint16_t cid; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_hdr; +#define L2CAP_HDR_SIZE 4 + +typedef struct { + uint8_t code; + uint8_t ident; + uint16_t len; +} __attribute__ ((packed)) l2cap_cmd_hdr; +#define L2CAP_CMD_HDR_SIZE 4 + +typedef struct { + uint16_t reason; +} __attribute__ ((packed)) l2cap_cmd_rej; +#define L2CAP_CMD_REJ_SIZE 2 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} __attribute__ ((packed)) l2cap_cmd_rej_cid; +#define L2CAP_CMD_REJ_CID_SIZE 4 + +/* reject reason */ +enum bt_l2cap_rej_reason { + L2CAP_REJ_CMD_NOT_UNDERSTOOD = 0, + L2CAP_REJ_SIG_TOOBIG, + L2CAP_REJ_CID_INVAL, +}; + +typedef struct { + uint16_t psm; + uint16_t scid; +} __attribute__ ((packed)) l2cap_conn_req; +#define L2CAP_CONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; + uint16_t result; + uint16_t status; +} __attribute__ ((packed)) l2cap_conn_rsp; +#define L2CAP_CONN_RSP_SIZE 8 + +/* connect result */ +enum bt_l2cap_conn_res { + L2CAP_CR_SUCCESS = 0, + L2CAP_CR_PEND, + L2CAP_CR_BAD_PSM, + L2CAP_CR_SEC_BLOCK, + L2CAP_CR_NO_MEM, +}; + +/* connect status */ +enum bt_l2cap_conn_stat { + L2CAP_CS_NO_INFO = 0, + L2CAP_CS_AUTHEN_PEND, + L2CAP_CS_AUTHOR_PEND, +}; + +typedef struct { + uint16_t dcid; + uint16_t flags; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_conf_req; +#define L2CAP_CONF_REQ_SIZE(datalen) (4 + (datalen)) + +typedef struct { + uint16_t scid; + uint16_t flags; + uint16_t result; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_conf_rsp; +#define L2CAP_CONF_RSP_SIZE(datalen) (6 + datalen) + +enum bt_l2cap_conf_res { + L2CAP_CONF_SUCCESS = 0, + L2CAP_CONF_UNACCEPT, + L2CAP_CONF_REJECT, + L2CAP_CONF_UNKNOWN, +}; + +typedef struct { + uint8_t type; + uint8_t len; + uint8_t val[0]; +} __attribute__ ((packed)) l2cap_conf_opt; +#define L2CAP_CONF_OPT_SIZE 2 + +enum bt_l2cap_conf_val { + L2CAP_CONF_MTU = 1, + L2CAP_CONF_FLUSH_TO, + L2CAP_CONF_QOS, + L2CAP_CONF_RFC, + L2CAP_CONF_RFC_MODE = L2CAP_CONF_RFC, +}; + +typedef struct { + uint8_t flags; + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t latency; + uint32_t delay_variation; +} __attribute__ ((packed)) l2cap_conf_opt_qos; +#define L2CAP_CONF_OPT_QOS_SIZE 22 + +enum bt_l2cap_conf_opt_qos_st { + L2CAP_CONF_QOS_NO_TRAFFIC = 0x00, + L2CAP_CONF_QOS_BEST_EFFORT, + L2CAP_CONF_QOS_GUARANTEED, +}; + +#define L2CAP_CONF_QOS_WILDCARD 0xffffffff + +enum bt_l2cap_mode { + L2CAP_MODE_BASIC = 0, + L2CAP_MODE_RETRANS = 1, + L2CAP_MODE_FLOWCTL = 2, +}; + +typedef struct { + uint16_t dcid; + uint16_t scid; +} __attribute__ ((packed)) l2cap_disconn_req; +#define L2CAP_DISCONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} __attribute__ ((packed)) l2cap_disconn_rsp; +#define L2CAP_DISCONN_RSP_SIZE 4 + +typedef struct { + uint16_t type; +} __attribute__ ((packed)) l2cap_info_req; +#define L2CAP_INFO_REQ_SIZE 2 + +typedef struct { + uint16_t type; + uint16_t result; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_info_rsp; +#define L2CAP_INFO_RSP_SIZE 4 + +/* info type */ +enum bt_l2cap_info_type { + L2CAP_IT_CL_MTU = 1, + L2CAP_IT_FEAT_MASK, +}; + +/* info result */ +enum bt_l2cap_info_result { + L2CAP_IR_SUCCESS = 0, + L2CAP_IR_NOTSUPP, +}; + +/* Service Discovery Protocol defines */ +/* Note that all multibyte values in lower layer protocols (above in this file) + * are little-endian while SDP is big-endian. */ + +/* Protocol UUIDs */ +enum sdp_proto_uuid { + SDP_UUID = 0x0001, + UDP_UUID = 0x0002, + RFCOMM_UUID = 0x0003, + TCP_UUID = 0x0004, + TCS_BIN_UUID = 0x0005, + TCS_AT_UUID = 0x0006, + OBEX_UUID = 0x0008, + IP_UUID = 0x0009, + FTP_UUID = 0x000a, + HTTP_UUID = 0x000c, + WSP_UUID = 0x000e, + BNEP_UUID = 0x000f, + UPNP_UUID = 0x0010, + HIDP_UUID = 0x0011, + HCRP_CTRL_UUID = 0x0012, + HCRP_DATA_UUID = 0x0014, + HCRP_NOTE_UUID = 0x0016, + AVCTP_UUID = 0x0017, + AVDTP_UUID = 0x0019, + CMTP_UUID = 0x001b, + UDI_UUID = 0x001d, + MCAP_CTRL_UUID = 0x001e, + MCAP_DATA_UUID = 0x001f, + L2CAP_UUID = 0x0100, +}; + +/* + * Service class identifiers of standard services and service groups + */ +enum service_class_id { + SDP_SERVER_SVCLASS_ID = 0x1000, + BROWSE_GRP_DESC_SVCLASS_ID = 0x1001, + PUBLIC_BROWSE_GROUP = 0x1002, + SERIAL_PORT_SVCLASS_ID = 0x1101, + LAN_ACCESS_SVCLASS_ID = 0x1102, + DIALUP_NET_SVCLASS_ID = 0x1103, + IRMC_SYNC_SVCLASS_ID = 0x1104, + OBEX_OBJPUSH_SVCLASS_ID = 0x1105, + OBEX_FILETRANS_SVCLASS_ID = 0x1106, + IRMC_SYNC_CMD_SVCLASS_ID = 0x1107, + HEADSET_SVCLASS_ID = 0x1108, + CORDLESS_TELEPHONY_SVCLASS_ID = 0x1109, + AUDIO_SOURCE_SVCLASS_ID = 0x110a, + AUDIO_SINK_SVCLASS_ID = 0x110b, + AV_REMOTE_TARGET_SVCLASS_ID = 0x110c, + ADVANCED_AUDIO_SVCLASS_ID = 0x110d, + AV_REMOTE_SVCLASS_ID = 0x110e, + VIDEO_CONF_SVCLASS_ID = 0x110f, + INTERCOM_SVCLASS_ID = 0x1110, + FAX_SVCLASS_ID = 0x1111, + HEADSET_AGW_SVCLASS_ID = 0x1112, + WAP_SVCLASS_ID = 0x1113, + WAP_CLIENT_SVCLASS_ID = 0x1114, + PANU_SVCLASS_ID = 0x1115, + NAP_SVCLASS_ID = 0x1116, + GN_SVCLASS_ID = 0x1117, + DIRECT_PRINTING_SVCLASS_ID = 0x1118, + REFERENCE_PRINTING_SVCLASS_ID = 0x1119, + IMAGING_SVCLASS_ID = 0x111a, + IMAGING_RESPONDER_SVCLASS_ID = 0x111b, + IMAGING_ARCHIVE_SVCLASS_ID = 0x111c, + IMAGING_REFOBJS_SVCLASS_ID = 0x111d, + HANDSFREE_SVCLASS_ID = 0x111e, + HANDSFREE_AGW_SVCLASS_ID = 0x111f, + DIRECT_PRT_REFOBJS_SVCLASS_ID = 0x1120, + REFLECTED_UI_SVCLASS_ID = 0x1121, + BASIC_PRINTING_SVCLASS_ID = 0x1122, + PRINTING_STATUS_SVCLASS_ID = 0x1123, + HID_SVCLASS_ID = 0x1124, + HCR_SVCLASS_ID = 0x1125, + HCR_PRINT_SVCLASS_ID = 0x1126, + HCR_SCAN_SVCLASS_ID = 0x1127, + CIP_SVCLASS_ID = 0x1128, + VIDEO_CONF_GW_SVCLASS_ID = 0x1129, + UDI_MT_SVCLASS_ID = 0x112a, + UDI_TA_SVCLASS_ID = 0x112b, + AV_SVCLASS_ID = 0x112c, + SAP_SVCLASS_ID = 0x112d, + PBAP_PCE_SVCLASS_ID = 0x112e, + PBAP_PSE_SVCLASS_ID = 0x112f, + PBAP_SVCLASS_ID = 0x1130, + PNP_INFO_SVCLASS_ID = 0x1200, + GENERIC_NETWORKING_SVCLASS_ID = 0x1201, + GENERIC_FILETRANS_SVCLASS_ID = 0x1202, + GENERIC_AUDIO_SVCLASS_ID = 0x1203, + GENERIC_TELEPHONY_SVCLASS_ID = 0x1204, + UPNP_SVCLASS_ID = 0x1205, + UPNP_IP_SVCLASS_ID = 0x1206, + UPNP_PAN_SVCLASS_ID = 0x1300, + UPNP_LAP_SVCLASS_ID = 0x1301, + UPNP_L2CAP_SVCLASS_ID = 0x1302, + VIDEO_SOURCE_SVCLASS_ID = 0x1303, + VIDEO_SINK_SVCLASS_ID = 0x1304, + VIDEO_DISTRIBUTION_SVCLASS_ID = 0x1305, + MDP_SVCLASS_ID = 0x1400, + MDP_SOURCE_SVCLASS_ID = 0x1401, + MDP_SINK_SVCLASS_ID = 0x1402, + APPLE_AGENT_SVCLASS_ID = 0x2112, +}; + +/* + * Standard profile descriptor identifiers; note these + * may be identical to some of the service classes defined above + */ +#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID +#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID +#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID +#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID +#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID +#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID +#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID +#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID +#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID +#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID +#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID +#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID +#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID +#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID +#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID +#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID +#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID +#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID +#define FAX_PROFILE_ID FAX_SVCLASS_ID +#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID +#define WAP_PROFILE_ID WAP_SVCLASS_ID +#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID +#define PANU_PROFILE_ID PANU_SVCLASS_ID +#define NAP_PROFILE_ID NAP_SVCLASS_ID +#define GN_PROFILE_ID GN_SVCLASS_ID +#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID +#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID +#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID +#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID +#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID +#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID +#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID +#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID +#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID +#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID +#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID +#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID +#define HID_PROFILE_ID HID_SVCLASS_ID +#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID +#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define CIP_PROFILE_ID CIP_SVCLASS_ID +#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID +#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID +#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID +#define AV_PROFILE_ID AV_SVCLASS_ID +#define SAP_PROFILE_ID SAP_SVCLASS_ID +#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID +#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID +#define PBAP_PROFILE_ID PBAP_SVCLASS_ID +#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID +#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID +#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID +#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID +#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID +#define UPNP_PROFILE_ID UPNP_SVCLASS_ID +#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID +#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID +#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID +#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID +#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID +#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID +#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID +#define MDP_PROFILE_ID MDP_SVCLASS_ID +#define MDP_SOURCE_PROFILE_ID MDP_SROUCE_SVCLASS_ID +#define MDP_SINK_PROFILE_ID MDP_SINK_SVCLASS_ID +#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID + +/* Data Representation */ +enum bt_sdp_data_type { + SDP_DTYPE_NIL = 0 << 3, + SDP_DTYPE_UINT = 1 << 3, + SDP_DTYPE_SINT = 2 << 3, + SDP_DTYPE_UUID = 3 << 3, + SDP_DTYPE_STRING = 4 << 3, + SDP_DTYPE_BOOL = 5 << 3, + SDP_DTYPE_SEQ = 6 << 3, + SDP_DTYPE_ALT = 7 << 3, + SDP_DTYPE_URL = 8 << 3, +}; + +enum bt_sdp_data_size { + SDP_DSIZE_1 = 0, + SDP_DSIZE_2, + SDP_DSIZE_4, + SDP_DSIZE_8, + SDP_DSIZE_16, + SDP_DSIZE_NEXT1, + SDP_DSIZE_NEXT2, + SDP_DSIZE_NEXT4, + SDP_DSIZE_MASK = SDP_DSIZE_NEXT4, +}; + +enum bt_sdp_cmd { + SDP_ERROR_RSP = 0x01, + SDP_SVC_SEARCH_REQ = 0x02, + SDP_SVC_SEARCH_RSP = 0x03, + SDP_SVC_ATTR_REQ = 0x04, + SDP_SVC_ATTR_RSP = 0x05, + SDP_SVC_SEARCH_ATTR_REQ = 0x06, + SDP_SVC_SEARCH_ATTR_RSP = 0x07, +}; + +enum bt_sdp_errorcode { + SDP_INVALID_VERSION = 0x0001, + SDP_INVALID_RECORD_HANDLE = 0x0002, + SDP_INVALID_SYNTAX = 0x0003, + SDP_INVALID_PDU_SIZE = 0x0004, + SDP_INVALID_CSTATE = 0x0005, +}; + +/* + * String identifiers are based on the SDP spec stating that + * "base attribute id of the primary (universal) language must be 0x0100" + * + * Other languages should have their own offset; e.g.: + * #define XXXLangBase yyyy + * #define AttrServiceName_XXX 0x0000+XXXLangBase + */ +#define SDP_PRIMARY_LANG_BASE 0x0100 + +enum bt_sdp_attribute_id { + SDP_ATTR_RECORD_HANDLE = 0x0000, + SDP_ATTR_SVCLASS_ID_LIST = 0x0001, + SDP_ATTR_RECORD_STATE = 0x0002, + SDP_ATTR_SERVICE_ID = 0x0003, + SDP_ATTR_PROTO_DESC_LIST = 0x0004, + SDP_ATTR_BROWSE_GRP_LIST = 0x0005, + SDP_ATTR_LANG_BASE_ATTR_ID_LIST = 0x0006, + SDP_ATTR_SVCINFO_TTL = 0x0007, + SDP_ATTR_SERVICE_AVAILABILITY = 0x0008, + SDP_ATTR_PFILE_DESC_LIST = 0x0009, + SDP_ATTR_DOC_URL = 0x000a, + SDP_ATTR_CLNT_EXEC_URL = 0x000b, + SDP_ATTR_ICON_URL = 0x000c, + SDP_ATTR_ADD_PROTO_DESC_LIST = 0x000d, + + SDP_ATTR_SVCNAME_PRIMARY = SDP_PRIMARY_LANG_BASE + 0, + SDP_ATTR_SVCDESC_PRIMARY = SDP_PRIMARY_LANG_BASE + 1, + SDP_ATTR_SVCPROV_PRIMARY = SDP_PRIMARY_LANG_BASE + 2, + + SDP_ATTR_GROUP_ID = 0x0200, + SDP_ATTR_IP_SUBNET = 0x0200, + + /* SDP */ + SDP_ATTR_VERSION_NUM_LIST = 0x0200, + SDP_ATTR_SVCDB_STATE = 0x0201, + + SDP_ATTR_SERVICE_VERSION = 0x0300, + SDP_ATTR_EXTERNAL_NETWORK = 0x0301, + SDP_ATTR_SUPPORTED_DATA_STORES_LIST = 0x0301, + SDP_ATTR_FAX_CLASS1_SUPPORT = 0x0302, + SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL = 0x0302, + SDP_ATTR_FAX_CLASS20_SUPPORT = 0x0303, + SDP_ATTR_SUPPORTED_FORMATS_LIST = 0x0303, + SDP_ATTR_FAX_CLASS2_SUPPORT = 0x0304, + SDP_ATTR_AUDIO_FEEDBACK_SUPPORT = 0x0305, + SDP_ATTR_NETWORK_ADDRESS = 0x0306, + SDP_ATTR_WAP_GATEWAY = 0x0307, + SDP_ATTR_HOMEPAGE_URL = 0x0308, + SDP_ATTR_WAP_STACK_TYPE = 0x0309, + SDP_ATTR_SECURITY_DESC = 0x030a, + SDP_ATTR_NET_ACCESS_TYPE = 0x030b, + SDP_ATTR_MAX_NET_ACCESSRATE = 0x030c, + SDP_ATTR_IP4_SUBNET = 0x030d, + SDP_ATTR_IP6_SUBNET = 0x030e, + SDP_ATTR_SUPPORTED_CAPABILITIES = 0x0310, + SDP_ATTR_SUPPORTED_FEATURES = 0x0311, + SDP_ATTR_SUPPORTED_FUNCTIONS = 0x0312, + SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY = 0x0313, + SDP_ATTR_SUPPORTED_REPOSITORIES = 0x0314, + + /* PnP Information */ + SDP_ATTR_SPECIFICATION_ID = 0x0200, + SDP_ATTR_VENDOR_ID = 0x0201, + SDP_ATTR_PRODUCT_ID = 0x0202, + SDP_ATTR_VERSION = 0x0203, + SDP_ATTR_PRIMARY_RECORD = 0x0204, + SDP_ATTR_VENDOR_ID_SOURCE = 0x0205, + + /* BT HID */ + SDP_ATTR_DEVICE_RELEASE_NUMBER = 0x0200, + SDP_ATTR_PARSER_VERSION = 0x0201, + SDP_ATTR_DEVICE_SUBCLASS = 0x0202, + SDP_ATTR_COUNTRY_CODE = 0x0203, + SDP_ATTR_VIRTUAL_CABLE = 0x0204, + SDP_ATTR_RECONNECT_INITIATE = 0x0205, + SDP_ATTR_DESCRIPTOR_LIST = 0x0206, + SDP_ATTR_LANG_ID_BASE_LIST = 0x0207, + SDP_ATTR_SDP_DISABLE = 0x0208, + SDP_ATTR_BATTERY_POWER = 0x0209, + SDP_ATTR_REMOTE_WAKEUP = 0x020a, + SDP_ATTR_PROFILE_VERSION = 0x020b, + SDP_ATTR_SUPERVISION_TIMEOUT = 0x020c, + SDP_ATTR_NORMALLY_CONNECTABLE = 0x020d, + SDP_ATTR_BOOT_DEVICE = 0x020e, +}; diff -Nur kvm-76/qemu/hw/cirrus_vga.c kvm-userspace/qemu/hw/cirrus_vga.c --- kvm-76/qemu/hw/cirrus_vga.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/cirrus_vga.c 2008-10-12 09:38:23.000000000 +0200 @@ -2628,40 +2628,34 @@ cirrus_linear_bitblt_writel, }; -void *set_vram_mapping(unsigned long begin, unsigned long end) +void set_vram_mapping(void *ptr, unsigned long begin, unsigned long end) { - void *vram_pointer = NULL; - /* align begin and end address */ begin = begin & TARGET_PAGE_MASK; end = begin + VGA_RAM_SIZE; end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; - if (kvm_enabled()) - vram_pointer = kvm_cpu_create_phys_mem(begin, end - begin, 1, 1); - - if (vram_pointer == NULL) { - printf("set_vram_mapping: cannot allocate memory: %m\n"); - return NULL; + if (kvm_enabled()) { + kvm_cpu_register_physical_memory(begin, end - begin, + ptr - (void *)phys_ram_base); + kvm_qemu_log_memory(begin, end - begin, 1); } - - memset(vram_pointer, 0, end - begin); - - return vram_pointer; } -int unset_vram_mapping(unsigned long begin, unsigned long end) +void unset_vram_mapping(void *ptr, unsigned long begin, unsigned long end) { /* align begin and end address */ end = begin + VGA_RAM_SIZE; begin = begin & TARGET_PAGE_MASK; end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; - if (kvm_enabled()) - kvm_cpu_destroy_phys_mem(begin, end - begin); - - return 0; + if (kvm_enabled()) { + kvm_qemu_log_memory(begin, end - begin, 0); + kvm_cpu_unregister_physical_memory(begin, end - begin, + ptr - (void *)phys_ram_base); + } } + #ifdef CONFIG_X86 static void kvm_update_vga_alias(CirrusVGAState *s, int ok, int bank) { @@ -2677,14 +2671,13 @@ if (!s->aliases_enabled || base != s->aliased_bank_base[bank] || limit != s->aliased_bank_limit[bank]) { - kvm_create_memory_alias(kvm_context, - 0xa0000 + bank * 0x8000, - limit, base); + kvm_qemu_create_memory_alias(0xa0000 + bank * 0x8000, + limit, base); s->aliased_bank_base[bank] = base; s->aliased_bank_limit[bank] = limit; } } else { - kvm_destroy_memory_alias(kvm_context, 0xa0000 + bank * 0x8000); + kvm_qemu_destroy_memory_alias(0xa0000 + bank * 0x8000); } } @@ -2721,17 +2714,8 @@ if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { if (kvm_enabled() && s->cirrus_lfb_addr && s->cirrus_lfb_end && !s->map_addr) { - void *vram_pointer, *old_vram; - - vram_pointer = set_vram_mapping(s->cirrus_lfb_addr, - s->cirrus_lfb_end); - if (!vram_pointer) - fprintf(stderr, "NULL vram_pointer\n"); - else { - old_vram = vga_update_vram((VGAState *)s, vram_pointer, - VGA_RAM_SIZE); - qemu_free(old_vram); - } + set_vram_mapping(s->vram_ptr, + s->cirrus_lfb_addr, s->cirrus_lfb_end); s->map_addr = s->cirrus_lfb_addr; s->map_end = s->cirrus_lfb_end; } @@ -2750,16 +2734,9 @@ generic_io: if (kvm_enabled() && s->cirrus_lfb_addr && s->cirrus_lfb_end && s->map_addr) { - int error; - void *old_vram = NULL; - - error = unset_vram_mapping(s->cirrus_lfb_addr, - s->cirrus_lfb_end); - if (!error) - old_vram = vga_update_vram((VGAState *)s, NULL, - VGA_RAM_SIZE); - if (old_vram) - munmap(old_vram, s->map_end - s->map_addr); + unset_vram_mapping(s->vram_ptr, + s->cirrus_lfb_addr, + s->cirrus_lfb_end); s->map_addr = s->map_end = 0; } s->cirrus_linear_write[0] = cirrus_linear_writeb; @@ -2868,8 +2845,7 @@ case 0x3ba: case 0x3da: /* just toggle to fool polling */ - s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE; - val = s->st01; + val = s->st01 = s->retrace((VGAState *) s); s->ar_flip_flop = 0; break; default: @@ -2932,6 +2908,7 @@ break; case 0x3c2: s->msr = val & ~0x10; + s->update_retrace_info((VGAState *) s); break; case 0x3c4: s->sr_index = val; @@ -2943,6 +2920,7 @@ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); #endif s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + if (s->sr_index == 1) s->update_retrace_info((VGAState *) s); break; case 0x3c6: cirrus_write_hidden_dac(s, val); @@ -3010,6 +2988,18 @@ s->cr[s->cr_index] = val; break; } + + switch(s->cr_index) { + case 0x00: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x11: + case 0x17: + s->update_retrace_info((VGAState *) s); + break; + } break; case 0x3ba: case 0x3da: diff -Nur kvm-76/qemu/hw/e1000.c kvm-userspace/qemu/hw/e1000.c --- kvm-76/qemu/hw/e1000.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/e1000.c 2008-10-12 09:38:23.000000000 +0200 @@ -103,8 +103,8 @@ uint32_t paylen; uint16_t tso_frames; char tse; - char ip; - char tcp; + int8_t ip; + int8_t tcp; char cptse; // current packet tse bit } tx; @@ -132,7 +132,7 @@ }; enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W }; -static char phy_regcap[0x20] = { +static const char phy_regcap[0x20] = { [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, @@ -784,7 +784,7 @@ (8 * (addr & 3))) & 0xffff; } -int mac_regtosave[] = { +static const int mac_regtosave[] = { CTRL, EECD, EERD, GPRC, GPTC, ICR, ICS, IMC, IMS, LEDCTL, MANC, MDIC, MPC, PBA, RCTL, RDBAH, RDBAL, RDH, RDLEN, RDT, STATUS, SWSM, TCTL, TDBAH, TDBAL, TDH, TDLEN, @@ -792,7 +792,7 @@ }; enum { MAC_NSAVE = sizeof mac_regtosave/sizeof *mac_regtosave }; -struct { +static const struct { int size; int array0; } mac_regarraystosave[] = { {32, RA}, {128, MTA} }; @@ -825,8 +825,8 @@ qemu_put_be16s(f, &s->tx.size); qemu_put_be16s(f, &s->tx.tso_frames); qemu_put_8s(f, &s->tx.sum_needed); - qemu_put_8s(f, &s->tx.ip); - qemu_put_8s(f, &s->tx.tcp); + qemu_put_s8s(f, &s->tx.ip); + qemu_put_s8s(f, &s->tx.tcp); qemu_put_buffer(f, s->tx.header, sizeof s->tx.header); qemu_put_buffer(f, s->tx.data, sizeof s->tx.data); for (i = 0; i < 64; i++) @@ -850,7 +850,7 @@ if ((ret = pci_device_load(&s->dev, f)) < 0) return ret; if (version_id == 1) - qemu_get_be32s(f, &i); /* once some unused instance id */ + qemu_get_sbe32s(f, &i); /* once some unused instance id */ qemu_get_be32s(f, &s->mmio_base); qemu_get_be32s(f, &s->rxbuf_size); qemu_get_be32s(f, &s->rxbuf_min_shift); @@ -871,8 +871,8 @@ qemu_get_be16s(f, &s->tx.size); qemu_get_be16s(f, &s->tx.tso_frames); qemu_get_8s(f, &s->tx.sum_needed); - qemu_get_8s(f, &s->tx.ip); - qemu_get_8s(f, &s->tx.tcp); + qemu_get_s8s(f, &s->tx.ip); + qemu_get_s8s(f, &s->tx.tcp); qemu_get_buffer(f, s->tx.header, sizeof s->tx.header); qemu_get_buffer(f, s->tx.data, sizeof s->tx.data); for (i = 0; i < 64; i++) @@ -888,7 +888,7 @@ return 0; } -static uint16_t e1000_eeprom_template[64] = { +static const uint16_t e1000_eeprom_template[64] = { 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x3000, 0x1000, 0x6403, E1000_DEVID, 0x8086, E1000_DEVID, 0x8086, 0x3040, 0x0008, 0x2000, 0x7e14, 0x0048, 0x1000, 0x00d8, 0x0000, 0x2700, @@ -899,7 +899,7 @@ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, }; -static uint16_t phy_reg_init[] = { +static const uint16_t phy_reg_init[] = { [PHY_CTRL] = 0x1140, [PHY_STATUS] = 0x796d, // link initially up [PHY_ID1] = 0x141, [PHY_ID2] = PHY_ID2_INIT, [PHY_1000T_CTRL] = 0x0e00, [M88E1000_PHY_SPEC_CTRL] = 0x360, @@ -908,7 +908,7 @@ [M88E1000_PHY_SPEC_STATUS] = 0xac00, }; -static uint32_t mac_reg_init[] = { +static const uint32_t mac_reg_init[] = { [PBA] = 0x00100030, [LEDCTL] = 0x602, [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | diff -Nur kvm-76/qemu/hw/esp.c kvm-userspace/qemu/hw/esp.c --- kvm-76/qemu/hw/esp.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/esp.c 2008-10-12 09:38:23.000000000 +0200 @@ -569,7 +569,7 @@ static CPUWriteMemoryFunc *esp_mem_write[3] = { esp_mem_writeb, NULL, - NULL, + esp_mem_writeb, }; static void esp_save(QEMUFile *f, void *opaque) @@ -578,7 +578,7 @@ qemu_put_buffer(f, s->rregs, ESP_REGS); qemu_put_buffer(f, s->wregs, ESP_REGS); - qemu_put_be32s(f, (uint32_t *)&s->ti_size); + qemu_put_sbe32s(f, &s->ti_size); qemu_put_be32s(f, &s->ti_rptr); qemu_put_be32s(f, &s->ti_wptr); qemu_put_buffer(f, s->ti_buf, TI_BUFSZ); @@ -600,7 +600,7 @@ qemu_get_buffer(f, s->rregs, ESP_REGS); qemu_get_buffer(f, s->wregs, ESP_REGS); - qemu_get_be32s(f, (uint32_t *)&s->ti_size); + qemu_get_sbe32s(f, &s->ti_size); qemu_get_be32s(f, &s->ti_rptr); qemu_get_be32s(f, &s->ti_wptr); qemu_get_buffer(f, s->ti_buf, TI_BUFSZ); diff -Nur kvm-76/qemu/hw/fw_cfg.c kvm-userspace/qemu/hw/fw_cfg.c --- kvm-76/qemu/hw/fw_cfg.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/fw_cfg.c 2008-10-12 09:38:23.000000000 +0200 @@ -259,7 +259,6 @@ { FWCfgState *s; int io_ctl_memory, io_data_memory; - extern int nographic; s = qemu_mallocz(sizeof(FWCfgState)); if (!s) diff -Nur kvm-76/qemu/hw/hw.h kvm-userspace/qemu/hw/hw.h --- kvm-76/qemu/hw/hw.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/hw.h 2008-10-12 09:38:23.000000000 +0200 @@ -7,26 +7,68 @@ /* VM Load/Save */ -typedef void (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, int64_t pos, int size); -typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, int64_t pos, int size); -typedef void (QEMUFileCloseFunc)(void *opaque); - -QEMUFile *qemu_fopen(void *opaque, QEMUFilePutBufferFunc *put_buffer, - QEMUFileGetBufferFunc *get_buffer, QEMUFileCloseFunc *close); -QEMUFile *qemu_fopen_file(const char *filename, const char *mode); +/* This function writes a chunk of data to a file at the given position. + * The pos argument can be ignored if the file is only being used for + * streaming. The handler should try to write all of the data it can. + */ +typedef void (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, + int64_t pos, int size); + +/* Read a chunk of data from a file at the given position. The pos argument + * can be ignored if the file is only be used for streaming. The number of + * bytes actually read should be returned. + */ +typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, + int64_t pos, int size); + +/* Close a file and return an error code */ +typedef int (QEMUFileCloseFunc)(void *opaque); + +/* Called to determine if the file has exceeded it's bandwidth allocation. The + * bandwidth capping is a soft limit, not a hard limit. + */ +typedef int (QEMUFileRateLimit)(void *opaque); + +QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, + QEMUFileGetBufferFunc *get_buffer, + QEMUFileCloseFunc *close, + QEMUFileRateLimit *rate_limit); +QEMUFile *qemu_fopen(const char *filename, const char *mode); QEMUFile *qemu_fopen_fd(int fd); void qemu_fflush(QEMUFile *f); -void qemu_fclose(QEMUFile *f); +int qemu_fclose(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); void qemu_put_byte(QEMUFile *f, int v); + +static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, (int)v); +} + +#define qemu_put_sbyte qemu_put_byte + void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); int qemu_get_byte(QEMUFile *f); + +static inline unsigned int qemu_get_ubyte(QEMUFile *f) +{ + return (unsigned int)qemu_get_byte(f); +} + +#define qemu_get_sbyte qemu_get_byte + unsigned int qemu_get_be16(QEMUFile *f); unsigned int qemu_get_be32(QEMUFile *f); uint64_t qemu_get_be64(QEMUFile *f); +int qemu_file_rate_limit(QEMUFile *f); + +/* Try to send any outstanding data. This function is useful when output is + * halted due to rate limiting or EAGAIN errors occur as it can be used to + * resume output. */ +void qemu_file_put_notify(QEMUFile *f); static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) { @@ -68,17 +110,106 @@ *pv = qemu_get_byte(f); } +// Signed versions for type safety +static inline void qemu_put_sbuffer(QEMUFile *f, const int8_t *buf, int size) +{ + qemu_put_buffer(f, (const uint8_t *)buf, size); +} + +static inline void qemu_put_sbe16(QEMUFile *f, int v) +{ + qemu_put_be16(f, (unsigned int)v); +} + +static inline void qemu_put_sbe32(QEMUFile *f, int v) +{ + qemu_put_be32(f, (unsigned int)v); +} + +static inline void qemu_put_sbe64(QEMUFile *f, int64_t v) +{ + qemu_put_be64(f, (uint64_t)v); +} + +static inline size_t qemu_get_sbuffer(QEMUFile *f, int8_t *buf, int size) +{ + return qemu_get_buffer(f, (uint8_t *)buf, size); +} + +static inline int qemu_get_sbe16(QEMUFile *f) +{ + return (int)qemu_get_be16(f); +} + +static inline int qemu_get_sbe32(QEMUFile *f) +{ + return (int)qemu_get_be32(f); +} + +static inline int64_t qemu_get_sbe64(QEMUFile *f) +{ + return (int64_t)qemu_get_be64(f); +} + +static inline void qemu_put_s8s(QEMUFile *f, const int8_t *pv) +{ + qemu_put_8s(f, (const uint8_t *)pv); +} + +static inline void qemu_put_sbe16s(QEMUFile *f, const int16_t *pv) +{ + qemu_put_be16s(f, (const uint16_t *)pv); +} + +static inline void qemu_put_sbe32s(QEMUFile *f, const int32_t *pv) +{ + qemu_put_be32s(f, (const uint32_t *)pv); +} + +static inline void qemu_put_sbe64s(QEMUFile *f, const int64_t *pv) +{ + qemu_put_be64s(f, (const uint64_t *)pv); +} + +static inline void qemu_get_s8s(QEMUFile *f, int8_t *pv) +{ + qemu_get_8s(f, (uint8_t *)pv); +} + +static inline void qemu_get_sbe16s(QEMUFile *f, int16_t *pv) +{ + qemu_get_be16s(f, (uint16_t *)pv); +} + +static inline void qemu_get_sbe32s(QEMUFile *f, int32_t *pv) +{ + qemu_get_be32s(f, (uint32_t *)pv); +} + +static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv) +{ + qemu_get_be64s(f, (uint64_t *)pv); +} + #ifdef NEED_CPU_H #if TARGET_LONG_BITS == 64 #define qemu_put_betl qemu_put_be64 #define qemu_get_betl qemu_get_be64 #define qemu_put_betls qemu_put_be64s #define qemu_get_betls qemu_get_be64s +#define qemu_put_sbetl qemu_put_sbe64 +#define qemu_get_sbetl qemu_get_sbe64 +#define qemu_put_sbetls qemu_put_sbe64s +#define qemu_get_sbetls qemu_get_sbe64s #else #define qemu_put_betl qemu_put_be32 #define qemu_get_betl qemu_get_be32 #define qemu_put_betls qemu_put_be32s #define qemu_get_betls qemu_get_be32s +#define qemu_put_sbetl qemu_put_sbe32 +#define qemu_get_sbetl qemu_get_sbe32 +#define qemu_put_sbetls qemu_put_sbe32s +#define qemu_get_sbetls qemu_get_sbe32s #endif #endif diff -Nur kvm-76/qemu/hw/ide.c kvm-userspace/qemu/hw/ide.c --- kvm-76/qemu/hw/ide.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ide.c 2008-10-12 09:38:23.000000000 +0200 @@ -739,6 +739,14 @@ s->error = ABRT_ERR; } +static inline void ide_dma_submit_check(IDEState *s, + BlockDriverCompletionFunc *dma_cb, BMDMAState *bm) +{ + if (bm->aiocb) + return; + dma_cb(bm, -1); +} + static inline void ide_set_irq(IDEState *s) { BMDMAState *bm = s->bmdma; @@ -818,6 +826,11 @@ } } +static void ide_rw_error(IDEState *s) { + ide_abort_command(s); + ide_set_irq(s); +} + static void ide_sector_read(IDEState *s) { int64_t sector_num; @@ -837,6 +850,10 @@ if (n > s->req_nb_sectors) n = s->req_nb_sectors; ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); + if (ret != 0) { + ide_rw_error(s); + return; + } ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); ide_set_irq(s); ide_set_sector(s, sector_num + n); @@ -844,6 +861,14 @@ } } +static void ide_dma_error(IDEState *s) +{ + ide_transfer_stop(s); + s->error = ABRT_ERR; + s->status = READY_STAT | ERR_STAT; + ide_set_irq(s); +} + /* return 0 if buffer completed */ static int dma_buf_rw(BMDMAState *bm, int is_write) { @@ -892,7 +917,6 @@ return 1; } -/* XXX: handle errors */ static void ide_read_dma_cb(void *opaque, int ret) { BMDMAState *bm = opaque; @@ -900,6 +924,11 @@ int n; int64_t sector_num; + if (ret < 0) { + ide_dma_error(s); + return; + } + n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { @@ -934,6 +963,7 @@ #endif bm->aiocb = bdrv_aio_read(s->bs, sector_num, s->io_buffer, n, ide_read_dma_cb, bm); + ide_dma_submit_check(s, ide_read_dma_cb, bm); } static void ide_sector_read_dma(IDEState *s) @@ -964,6 +994,11 @@ if (n > s->req_nb_sectors) n = s->req_nb_sectors; ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); + if (ret != 0) { + ide_rw_error(s); + return; + } + s->nsector -= n; if (s->nsector == 0) { /* no more sectors to write */ @@ -993,7 +1028,6 @@ } } -/* XXX: handle errors */ static void ide_write_dma_cb(void *opaque, int ret) { BMDMAState *bm = opaque; @@ -1001,6 +1035,11 @@ int n; int64_t sector_num; + if (ret < 0) { + ide_dma_error(s); + return; + } + n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { @@ -1036,6 +1075,7 @@ #endif bm->aiocb = bdrv_aio_write(s->bs, sector_num, s->io_buffer, n, ide_write_dma_cb, bm); + ide_dma_submit_check(s, ide_write_dma_cb, bm); } static void ide_sector_write_dma(IDEState *s) diff -Nur kvm-76/qemu/hw/mips_malta.c kvm-userspace/qemu/hw/mips_malta.c --- kvm-76/qemu/hw/mips_malta.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/mips_malta.c 2008-10-12 09:38:23.000000000 +0200 @@ -36,6 +36,7 @@ #include "sysemu.h" #include "audio/audio.h" #include "boards.h" +#include "qemu-log.h" //#define DEBUG_BOARD_INIT @@ -59,8 +60,6 @@ #define MAX_IDE_BUS 2 -extern FILE *logfile; - typedef struct { uint32_t leds; uint32_t brk; diff -Nur kvm-76/qemu/hw/mips_r4k.c kvm-userspace/qemu/hw/mips_r4k.c --- kvm-76/qemu/hw/mips_r4k.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/mips_r4k.c 2008-10-12 09:38:23.000000000 +0200 @@ -15,6 +15,7 @@ #include "sysemu.h" #include "boards.h" #include "flash.h" +#include "qemu-log.h" #ifdef TARGET_WORDS_BIGENDIAN #define BIOS_FILENAME "mips_bios.bin" @@ -35,8 +36,6 @@ static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; -extern FILE *logfile; - static PITState *pit; /* PIT i8254 */ /* i8254 PIT is attached to the IRQ0 at PIC i8259 */ diff -Nur kvm-76/qemu/hw/nseries.c kvm-userspace/qemu/hw/nseries.c --- kvm-76/qemu/hw/nseries.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/nseries.c 2008-10-12 09:38:23.000000000 +0200 @@ -31,6 +31,7 @@ #include "devices.h" #include "flash.h" #include "hw.h" +#include "bt.h" /* Nokia N8x0 support */ struct n800_s { @@ -122,10 +123,6 @@ #define N8X0_BD_ADDR 0x00, 0x1a, 0x89, 0x9e, 0x3e, 0x81 -typedef struct { - uint8_t b[6]; -} __attribute__((packed)) bdaddr_t; /* XXX: move to BT headers */ - static void n800_mmc_cs_cb(void *opaque, int line, int level) { /* TODO: this seems to actually be connected to the menelaus, to @@ -744,6 +741,20 @@ cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); } +static void n8x0_uart_setup(struct n800_s *s) +{ + CharDriverState *radio = uart_hci_init( + omap2_gpio_in_get(s->cpu->gpif, + N8X0_BT_HOST_WKUP_GPIO)[0]); + + omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_RESET_GPIO, + csrhci_pins_get(radio)[csrhci_pin_reset]); + omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_WKUP_GPIO, + csrhci_pins_get(radio)[csrhci_pin_wakeup]); + + omap_uart_attach(s->cpu->uart[BT_UART], radio); +} + static void n8x0_usb_power_cb(void *opaque, int line, int level) { struct n800_s *s = opaque; @@ -1309,6 +1320,7 @@ n8x0_spi_setup(s); n8x0_dss_setup(s, ds); n8x0_cbus_setup(s); + n8x0_uart_setup(s); if (usb_enabled) n8x0_usb_setup(s); diff -Nur kvm-76/qemu/hw/pc.h kvm-userspace/qemu/hw/pc.h --- kvm-76/qemu/hw/pc.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pc.h 2008-10-12 09:38:23.000000000 +0200 @@ -113,6 +113,12 @@ int piix4_init(PCIBus *bus, int devfn); /* vga.c */ +enum vga_retrace_method { + VGA_RETRACE_DUMB, + VGA_RETRACE_PRECISE +}; + +extern enum vga_retrace_method vga_retrace_method; #ifndef TARGET_SPARC #define VGA_RAM_SIZE (16 * 1024 * 1024) diff -Nur kvm-76/qemu/hw/pci.c kvm-userspace/qemu/hw/pci.c --- kvm-76/qemu/hw/pci.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pci.c 2008-10-12 09:38:23.000000000 +0200 @@ -568,7 +568,7 @@ const char *desc; } pci_class_desc; -static pci_class_desc pci_class_descriptions[] = +static const pci_class_desc pci_class_descriptions[] = { { 0x0100, "SCSI controller"}, { 0x0101, "IDE controller"}, @@ -612,7 +612,7 @@ { int i, class; PCIIORegion *r; - pci_class_desc *desc; + const pci_class_desc *desc; term_printf(" Bus %2d, device %3d, function %d:\n", d->bus->bus_num, d->devfn >> 3, d->devfn & 7); diff -Nur kvm-76/qemu/hw/pcnet.c kvm-userspace/qemu/hw/pcnet.c --- kvm-76/qemu/hw/pcnet.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pcnet.c 2008-10-12 09:38:23.000000000 +0200 @@ -1877,9 +1877,9 @@ if (s->pci_dev) pci_device_save(s->pci_dev, f); - qemu_put_be32(f, s->rap); - qemu_put_be32(f, s->isr); - qemu_put_be32(f, s->lnkst); + qemu_put_sbe32(f, s->rap); + qemu_put_sbe32(f, s->isr); + qemu_put_sbe32(f, s->lnkst); qemu_put_be32s(f, &s->rdra); qemu_put_be32s(f, &s->tdra); qemu_put_buffer(f, s->prom, 16); @@ -1888,10 +1888,10 @@ for (i = 0; i < 32; i++) qemu_put_be16s(f, &s->bcr[i]); qemu_put_be64s(f, &s->timer); - qemu_put_be32(f, s->xmit_pos); - qemu_put_be32(f, s->recv_pos); + qemu_put_sbe32(f, s->xmit_pos); + qemu_put_sbe32(f, s->recv_pos); qemu_put_buffer(f, s->buffer, 4096); - qemu_put_be32(f, s->tx_busy); + qemu_put_sbe32(f, s->tx_busy); qemu_put_timer(f, s->poll_timer); } @@ -1909,9 +1909,9 @@ return ret; } - qemu_get_be32s(f, (uint32_t*)&s->rap); - qemu_get_be32s(f, (uint32_t*)&s->isr); - qemu_get_be32s(f, (uint32_t*)&s->lnkst); + qemu_get_sbe32s(f, &s->rap); + qemu_get_sbe32s(f, &s->isr); + qemu_get_sbe32s(f, &s->lnkst); qemu_get_be32s(f, &s->rdra); qemu_get_be32s(f, &s->tdra); qemu_get_buffer(f, s->prom, 16); @@ -1920,10 +1920,10 @@ for (i = 0; i < 32; i++) qemu_get_be16s(f, &s->bcr[i]); qemu_get_be64s(f, &s->timer); - qemu_get_be32s(f, (uint32_t*)&s->xmit_pos); - qemu_get_be32s(f, (uint32_t*)&s->recv_pos); + qemu_get_sbe32s(f, &s->xmit_pos); + qemu_get_sbe32s(f, &s->recv_pos); qemu_get_buffer(f, s->buffer, 4096); - qemu_get_be32s(f, (uint32_t*)&s->tx_busy); + qemu_get_sbe32s(f, &s->tx_busy); qemu_get_timer(f, s->poll_timer); return 0; diff -Nur kvm-76/qemu/hw/pflash_cfi01.c kvm-userspace/qemu/hw/pflash_cfi01.c --- kvm-76/qemu/hw/pflash_cfi01.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pflash_cfi01.c 2008-10-12 09:38:23.000000000 +0200 @@ -111,8 +111,8 @@ else if (pfl->width == 4) boff = boff >> 2; - DPRINTF("%s: reading offset " TARGET_FMT_lx " under cmd %02x\n", - __func__, boff, pfl->cmd); + DPRINTF("%s: reading offset " TARGET_FMT_lx " under cmd %02x width %d\n", + __func__, offset, pfl->cmd, width); switch (pfl->cmd) { case 0x00: @@ -205,7 +205,7 @@ cmd = value; offset -= pfl->base; - DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d wcycle 0x%x\n", + DPRINTF("%s: writing offset " TARGET_FMT_lx " value %08x width %d wcycle 0x%x\n", __func__, offset, value, width, pfl->wcycle); /* Set the device in I/O access mode */ @@ -267,7 +267,7 @@ case 0x20: /* Block erase */ case 0x28: if (cmd == 0xd0) { /* confirm */ - pfl->wcycle = 1; + pfl->wcycle = 0; pfl->status |= 0x80; } else if (cmd == 0xff) { /* read array mode */ goto reset_flash; diff -Nur kvm-76/qemu/hw/ppc.c kvm-userspace/qemu/hw/ppc.c --- kvm-76/qemu/hw/ppc.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ppc.c 2008-10-12 09:38:23.000000000 +0200 @@ -26,13 +26,11 @@ #include "qemu-timer.h" #include "sysemu.h" #include "nvram.h" +#include "qemu-log.h" //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB -extern FILE *logfile; -extern int loglevel; - static void cpu_ppc_tb_stop (CPUState *env); static void cpu_ppc_tb_start (CPUState *env); diff -Nur kvm-76/qemu/hw/ppc405_boards.c kvm-userspace/qemu/hw/ppc405_boards.c --- kvm-76/qemu/hw/ppc405_boards.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ppc405_boards.c 2008-10-12 09:38:24.000000000 +0200 @@ -29,9 +29,7 @@ #include "sysemu.h" #include "block.h" #include "boards.h" - -extern int loglevel; -extern FILE *logfile; +#include "qemu-log.h" #define BIOS_FILENAME "ppc405_rom.bin" #undef BIOS_SIZE diff -Nur kvm-76/qemu/hw/ppc405_uc.c kvm-userspace/qemu/hw/ppc405_uc.c --- kvm-76/qemu/hw/ppc405_uc.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ppc405_uc.c 2008-10-12 09:38:24.000000000 +0200 @@ -27,9 +27,7 @@ #include "pc.h" #include "qemu-timer.h" #include "sysemu.h" - -extern int loglevel; -extern FILE *logfile; +#include "qemu-log.h" #define DEBUG_OPBA #define DEBUG_SDRAM diff -Nur kvm-76/qemu/hw/ppc4xx_devs.c kvm-userspace/qemu/hw/ppc4xx_devs.c --- kvm-76/qemu/hw/ppc4xx_devs.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ppc4xx_devs.c 2008-10-12 09:38:24.000000000 +0200 @@ -28,12 +28,10 @@ #include "ppc.h" #include "ppc4xx.h" #include "sysemu.h" +#include "qemu-log.h" #include "pci.h" #include "bswap.h" -extern int loglevel; -extern FILE *logfile; - //#define DEBUG_MMIO //#define DEBUG_UNASSIGNED #define DEBUG_UIC diff -Nur kvm-76/qemu/hw/ppc_prep.c kvm-userspace/qemu/hw/ppc_prep.c --- kvm-76/qemu/hw/ppc_prep.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ppc_prep.c 2008-10-12 09:38:24.000000000 +0200 @@ -31,6 +31,7 @@ #include "pci.h" #include "ppc.h" #include "boards.h" +#include "qemu-log.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO @@ -44,9 +45,6 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 -extern int loglevel; -extern FILE *logfile; - #if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) #define DEBUG_PPC_IO #endif diff -Nur kvm-76/qemu/hw/ptimer.c kvm-userspace/qemu/hw/ptimer.c --- kvm-76/qemu/hw/ptimer.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/ptimer.c 2008-10-12 09:38:24.000000000 +0200 @@ -165,9 +165,9 @@ qemu_put_be64s(f, &s->limit); qemu_put_be64s(f, &s->delta); qemu_put_be32s(f, &s->period_frac); - qemu_put_be64s(f, &s->period); - qemu_put_be64s(f, &s->last_event); - qemu_put_be64s(f, &s->next_event); + qemu_put_sbe64s(f, &s->period); + qemu_put_sbe64s(f, &s->last_event); + qemu_put_sbe64s(f, &s->next_event); qemu_put_timer(f, s->timer); } @@ -177,9 +177,9 @@ qemu_get_be64s(f, &s->limit); qemu_get_be64s(f, &s->delta); qemu_get_be32s(f, &s->period_frac); - qemu_get_be64s(f, &s->period); - qemu_get_be64s(f, &s->last_event); - qemu_get_be64s(f, &s->next_event); + qemu_get_sbe64s(f, &s->period); + qemu_get_sbe64s(f, &s->last_event); + qemu_get_sbe64s(f, &s->next_event); qemu_get_timer(f, s->timer); } diff -Nur kvm-76/qemu/hw/pxa2xx.c kvm-userspace/qemu/hw/pxa2xx.c --- kvm-76/qemu/hw/pxa2xx.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pxa2xx.c 2008-10-12 09:38:24.000000000 +0200 @@ -1233,9 +1233,9 @@ qemu_put_be32s(f, &s->last_rycr); qemu_put_be32s(f, &s->last_swcr); qemu_put_be32s(f, &s->last_rtcpicr); - qemu_put_be64s(f, (uint64_t *) &s->last_hz); - qemu_put_be64s(f, (uint64_t *) &s->last_sw); - qemu_put_be64s(f, (uint64_t *) &s->last_pi); + qemu_put_sbe64s(f, &s->last_hz); + qemu_put_sbe64s(f, &s->last_sw); + qemu_put_sbe64s(f, &s->last_pi); } static int pxa2xx_rtc_load(QEMUFile *f, void *opaque, int version_id) @@ -1257,9 +1257,9 @@ qemu_get_be32s(f, &s->last_rycr); qemu_get_be32s(f, &s->last_swcr); qemu_get_be32s(f, &s->last_rtcpicr); - qemu_get_be64s(f, (uint64_t *) &s->last_hz); - qemu_get_be64s(f, (uint64_t *) &s->last_sw); - qemu_get_be64s(f, (uint64_t *) &s->last_pi); + qemu_get_sbe64s(f, &s->last_hz); + qemu_get_sbe64s(f, &s->last_sw); + qemu_get_sbe64s(f, &s->last_pi); pxa2xx_rtc_alarm_update(s, s->rtsr); diff -Nur kvm-76/qemu/hw/pxa2xx_timer.c kvm-userspace/qemu/hw/pxa2xx_timer.c --- kvm-76/qemu/hw/pxa2xx_timer.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/pxa2xx_timer.c 2008-10-12 09:38:24.000000000 +0200 @@ -384,8 +384,8 @@ for (i = 0; i < 8; i ++) { qemu_put_be32s(f, &s->tm4[i].tm.value); qemu_put_be32(f, s->tm4[i].tm.level); - qemu_put_be32s(f, (uint32_t *) &s->tm4[i].oldclock); - qemu_put_be32s(f, (uint32_t *) &s->tm4[i].clock); + qemu_put_sbe32s(f, &s->tm4[i].oldclock); + qemu_put_sbe32s(f, &s->tm4[i].clock); qemu_put_be64s(f, &s->tm4[i].lastload); qemu_put_be32s(f, &s->tm4[i].freq); qemu_put_be32s(f, &s->tm4[i].control); @@ -418,8 +418,8 @@ for (i = 0; i < 8; i ++) { qemu_get_be32s(f, &s->tm4[i].tm.value); s->tm4[i].tm.level = qemu_get_be32(f); - qemu_get_be32s(f, (uint32_t *) &s->tm4[i].oldclock); - qemu_get_be32s(f, (uint32_t *) &s->tm4[i].clock); + qemu_get_sbe32s(f, &s->tm4[i].oldclock); + qemu_get_sbe32s(f, &s->tm4[i].clock); qemu_get_be64s(f, &s->tm4[i].lastload); qemu_get_be32s(f, &s->tm4[i].freq); qemu_get_be32s(f, &s->tm4[i].control); diff -Nur kvm-76/qemu/hw/realview.c kvm-userspace/qemu/hw/realview.c --- kvm-76/qemu/hw/realview.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/realview.c 2008-10-12 09:38:24.000000000 +0200 @@ -198,8 +198,9 @@ } QEMUMachine realview_machine = { - "realview", - "ARM RealView Emulation Baseboard (ARM926EJ-S)", - realview_init, - 0x1000 + .name = "realview", + .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)", + .init = realview_init, + .ram_require = 0x1000, + .use_scsi = 1, }; diff -Nur kvm-76/qemu/hw/sun4m.c kvm-userspace/qemu/hw/sun4m.c --- kvm-76/qemu/hw/sun4m.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/sun4m.c 2008-10-12 09:38:24.000000000 +0200 @@ -175,8 +175,6 @@ return 0; } -extern int nographic; - static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline, const char *boot_devices, ram_addr_t RAM_size, uint32_t kernel_size, @@ -693,7 +691,7 @@ slavio_serial_init(hwdef->serial_base, slavio_irq[hwdef->ser_irq], serial_hds[1], serial_hds[0]); - slavio_misc = slavio_misc_init(-1, hwdef->apc_base, + slavio_misc = slavio_misc_init(0, hwdef->apc_base, hwdef->aux1_base, hwdef->aux2_base, slavio_irq[hwdef->me_irq], env, &fdc_tc); @@ -1292,6 +1290,7 @@ .init = ss5_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss10_machine = { @@ -1300,6 +1299,7 @@ .init = ss10_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss600mp_machine = { @@ -1308,6 +1308,7 @@ .init = ss600mp_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss20_machine = { @@ -1316,6 +1317,7 @@ .init = ss20_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss2_machine = { @@ -1324,6 +1326,7 @@ .init = ss2_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine voyager_machine = { @@ -1332,6 +1335,7 @@ .init = vger_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss_lx_machine = { @@ -1340,6 +1344,7 @@ .init = ss_lx_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss4_machine = { @@ -1348,6 +1353,7 @@ .init = ss4_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine scls_machine = { @@ -1356,6 +1362,7 @@ .init = scls_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine sbook_machine = { @@ -1364,6 +1371,7 @@ .init = sbook_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; static const struct sun4d_hwdef sun4d_hwdefs[] = { @@ -1611,6 +1619,7 @@ .init = ss1000_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; QEMUMachine ss2000_machine = { @@ -1619,4 +1628,5 @@ .init = ss2000_init, .ram_require = PROM_SIZE_MAX + TCX_SIZE, .nodisk_ok = 1, + .use_scsi = 1, }; diff -Nur kvm-76/qemu/hw/sun4u.c kvm-userspace/qemu/hw/sun4u.c --- kvm-76/qemu/hw/sun4u.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/sun4u.c 2008-10-12 09:38:24.000000000 +0200 @@ -46,7 +46,6 @@ #define CMDLINE_ADDR 0x003ff000 #define INITRD_LOAD_ADDR 0x00300000 #define PROM_SIZE_MAX (4 * 1024 * 1024) -#define PROM_ADDR 0x1fff0000000ULL #define PROM_VADDR 0x000ffd00000ULL #define APB_SPECIAL_BASE 0x1fe00000000ULL #define APB_MEM_BASE 0x1ff00000000ULL @@ -61,6 +60,8 @@ struct hwdef { const char * const default_cpu_model; uint16_t machine_id; + uint64_t prom_addr; + uint64_t console_serial_base; }; int DMA_get_channel_mode (int nchan) @@ -107,8 +108,6 @@ return 0; } -extern int nographic; - static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, const char *arch, ram_addr_t RAM_size, @@ -260,9 +259,15 @@ { } +typedef struct ResetData { + CPUState *env; + uint64_t reset_addr; +} ResetData; + static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; cpu_reset(env); ptimer_set_limit(env->tick, 0x7fffffffffffffffULL, 1); @@ -271,6 +276,11 @@ ptimer_run(env->stick, 0); ptimer_set_limit(env->hstick, 0x7fffffffffffffffULL, 1); ptimer_run(env->hstick, 0); + env->gregs[1] = 0; // Memory start + env->gregs[2] = ram_size; // Memory size + env->gregs[3] = 0; // Machine description XXX + env->pc = s->reset_addr; + env->npc = env->pc + 4; } static void tick_irq(void *opaque) @@ -297,6 +307,21 @@ cpu_interrupt(env, CPU_INTERRUPT_TIMER); } +void cpu_tick_set_count(void *opaque, uint64_t count) +{ + ptimer_set_count(opaque, -count); +} + +uint64_t cpu_tick_get_count(void *opaque) +{ + return -ptimer_get_count(opaque); +} + +void cpu_tick_set_limit(void *opaque, uint64_t limit) +{ + ptimer_set_limit(opaque, -limit, 0); +} + static const int ide_iobase[2] = { 0x1f0, 0x170 }; static const int ide_iobase2[2] = { 0x3f6, 0x376 }; static const int ide_irq[2] = { 14, 15 }; @@ -328,6 +353,7 @@ BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BlockDriverState *fd[MAX_FD]; void *fw_cfg; + ResetData *reset_info; linux_boot = (kernel_filename != NULL); @@ -351,14 +377,21 @@ bh = qemu_bh_new(hstick_irq, env); env->hstick = ptimer_init(bh); ptimer_set_period(env->hstick, 1ULL); - qemu_register_reset(main_cpu_reset, env); - main_cpu_reset(env); + + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + reset_info->reset_addr = hwdef->prom_addr + 0x40ULL; + qemu_register_reset(main_cpu_reset, reset_info); + main_cpu_reset(reset_info); + // Override warm reset address with cold start address + env->pc = hwdef->prom_addr + 0x20ULL; + env->npc = env->pc + 4; /* allocate RAM */ cpu_register_physical_memory(0, RAM_size, 0); prom_offset = RAM_size + vga_ram_size; - cpu_register_physical_memory(PROM_ADDR, + cpu_register_physical_memory(hwdef->prom_addr, (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, prom_offset | IO_MEM_ROM); @@ -366,11 +399,16 @@ if (bios_name == NULL) bios_name = PROM_FILENAME; snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name); - ret = load_elf(buf, PROM_ADDR - PROM_VADDR, NULL, NULL, NULL); + ret = load_elf(buf, hwdef->prom_addr - PROM_VADDR, NULL, NULL, NULL); if (ret < 0) { - fprintf(stderr, "qemu: could not load prom '%s'\n", - buf); - exit(1); + ret = load_image_targphys(buf, hwdef->prom_addr, + (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & + TARGET_PAGE_MASK); + if (ret < 0) { + fprintf(stderr, "qemu: could not load prom '%s'\n", + buf); + exit(1); + } } kernel_size = 0; @@ -417,7 +455,13 @@ pci_cirrus_vga_init(pci_bus, ds, phys_ram_base + RAM_size, RAM_size, vga_ram_size); - for(i = 0; i < MAX_SERIAL_PORTS; i++) { + i = 0; + if (hwdef->console_serial_base) { + serial_mm_init(hwdef->console_serial_base, 0, NULL, 115200, + serial_hds[i], 1); + i++; + } + for(; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { serial_init(serial_io[i], NULL/*serial_irq[i]*/, 115200, serial_hds[i]); @@ -483,6 +527,7 @@ enum { sun4u_id = 0, sun4v_id = 64, + niagara_id, }; static const struct hwdef hwdefs[] = { @@ -490,11 +535,22 @@ { .default_cpu_model = "TI UltraSparc II", .machine_id = sun4u_id, + .prom_addr = 0x1fff0000000ULL, + .console_serial_base = 0, }, /* Sun4v generic PC-like machine */ { .default_cpu_model = "Sun UltraSparc T1", .machine_id = sun4v_id, + .prom_addr = 0x1fff0000000ULL, + .console_serial_base = 0, + }, + /* Sun4v generic Niagara machine */ + { + .default_cpu_model = "Sun UltraSparc T1", + .machine_id = niagara_id, + .prom_addr = 0xfff0000000ULL, + .console_serial_base = 0xfff0c2c000ULL, }, }; @@ -518,6 +574,16 @@ kernel_cmdline, initrd_filename, cpu_model, &hwdefs[1]); } +/* Niagara hardware initialisation */ +static void niagara_init(ram_addr_t RAM_size, int vga_ram_size, + const char *boot_devices, DisplayState *ds, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + sun4uv_init(RAM_size, vga_ram_size, boot_devices, ds, kernel_filename, + kernel_cmdline, initrd_filename, cpu_model, &hwdefs[2]); +} + QEMUMachine sun4u_machine = { .name = "sun4u", .desc = "Sun4u platform", @@ -533,3 +599,11 @@ .ram_require = PROM_SIZE_MAX + VGA_RAM_SIZE, .nodisk_ok = 1, }; + +QEMUMachine niagara_machine = { + .name = "Niagara", + .desc = "Sun4v platform, Niagara", + .init = niagara_init, + .ram_require = PROM_SIZE_MAX + VGA_RAM_SIZE, + .nodisk_ok = 1, +}; diff -Nur kvm-76/qemu/hw/tcx.c kvm-userspace/qemu/hw/tcx.c --- kvm-76/qemu/hw/tcx.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/tcx.c 2008-10-12 09:38:24.000000000 +0200 @@ -372,9 +372,9 @@ { TCXState *s = opaque; - qemu_put_be16s(f, (uint16_t *)&s->height); - qemu_put_be16s(f, (uint16_t *)&s->width); - qemu_put_be16s(f, (uint16_t *)&s->depth); + qemu_put_be16s(f, &s->height); + qemu_put_be16s(f, &s->width); + qemu_put_be16s(f, &s->depth); qemu_put_buffer(f, s->r, 256); qemu_put_buffer(f, s->g, 256); qemu_put_buffer(f, s->b, 256); @@ -391,13 +391,13 @@ return -EINVAL; if (version_id == 3) { - qemu_get_be32s(f, (uint32_t *)&dummy); - qemu_get_be32s(f, (uint32_t *)&dummy); - qemu_get_be32s(f, (uint32_t *)&dummy); + qemu_get_be32s(f, &dummy); + qemu_get_be32s(f, &dummy); + qemu_get_be32s(f, &dummy); } - qemu_get_be16s(f, (uint16_t *)&s->height); - qemu_get_be16s(f, (uint16_t *)&s->width); - qemu_get_be16s(f, (uint16_t *)&s->depth); + qemu_get_be16s(f, &s->height); + qemu_get_be16s(f, &s->width); + qemu_get_be16s(f, &s->depth); qemu_get_buffer(f, s->r, 256); qemu_get_buffer(f, s->g, 256); qemu_get_buffer(f, s->b, 256); diff -Nur kvm-76/qemu/hw/tmp105.c kvm-userspace/qemu/hw/tmp105.c --- kvm-76/qemu/hw/tmp105.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/tmp105.c 2008-10-12 09:38:24.000000000 +0200 @@ -185,9 +185,9 @@ qemu_put_8s(f, &s->pointer); qemu_put_8s(f, &s->config); - qemu_put_be16s(f, (uint16_t *) &s->temperature); - qemu_put_be16s(f, (uint16_t *) &s->limit[0]); - qemu_put_be16s(f, (uint16_t *) &s->limit[1]); + qemu_put_sbe16s(f, &s->temperature); + qemu_put_sbe16s(f, &s->limit[0]); + qemu_put_sbe16s(f, &s->limit[1]); qemu_put_byte(f, s->alarm); s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ @@ -204,9 +204,9 @@ qemu_get_8s(f, &s->pointer); qemu_get_8s(f, &s->config); - qemu_get_be16s(f, (uint16_t *) &s->temperature); - qemu_get_be16s(f, (uint16_t *) &s->limit[0]); - qemu_get_be16s(f, (uint16_t *) &s->limit[1]); + qemu_get_sbe16s(f, &s->temperature); + qemu_get_sbe16s(f, &s->limit[0]); + qemu_get_sbe16s(f, &s->limit[1]); s->alarm = qemu_get_byte(f); tmp105_interrupt_update(s); diff -Nur kvm-76/qemu/hw/tsc210x.c kvm-userspace/qemu/hw/tsc210x.c --- kvm-76/qemu/hw/tsc210x.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/tsc210x.c 2008-10-12 09:38:24.000000000 +0200 @@ -1046,8 +1046,8 @@ qemu_put_be16s(f, &s->pll[0]); qemu_put_be16s(f, &s->pll[1]); qemu_put_be16s(f, &s->volume); - qemu_put_be64(f, (uint64_t) (s->volume_change - now)); - qemu_put_be64(f, (uint64_t) (s->powerdown - now)); + qemu_put_sbe64(f, (s->volume_change - now)); + qemu_put_sbe64(f, (s->powerdown - now)); qemu_put_byte(f, s->softstep); qemu_put_be16s(f, &s->dac_power); @@ -1092,8 +1092,8 @@ qemu_get_be16s(f, &s->pll[0]); qemu_get_be16s(f, &s->pll[1]); qemu_get_be16s(f, &s->volume); - s->volume_change = (int64_t) qemu_get_be64(f) + now; - s->powerdown = (int64_t) qemu_get_be64(f) + now; + s->volume_change = qemu_get_sbe64(f) + now; + s->powerdown = qemu_get_sbe64(f) + now; s->softstep = qemu_get_byte(f); qemu_get_be16s(f, &s->dac_power); diff -Nur kvm-76/qemu/hw/usb-bt.c kvm-userspace/qemu/hw/usb-bt.c --- kvm-76/qemu/hw/usb-bt.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/hw/usb-bt.c 2008-10-12 09:38:24.000000000 +0200 @@ -0,0 +1,647 @@ +/* + * QEMU Bluetooth HCI USB Transport Layer v1.0 + * + * Copyright (C) 2007 OpenMoko, Inc. + * Copyright (C) 2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "qemu-common.h" +#include "usb.h" +#include "net.h" +#include "bt.h" + +struct USBBtState { + USBDevice dev; + struct HCIInfo *hci; + + int altsetting; + int config; + +#define CFIFO_LEN_MASK 255 +#define DFIFO_LEN_MASK 4095 + struct usb_hci_in_fifo_s { + uint8_t data[(DFIFO_LEN_MASK + 1) * 2]; + struct { + uint8_t *data; + int len; + } fifo[CFIFO_LEN_MASK + 1]; + int dstart, dlen, dsize, start, len; + } evt, acl, sco; + + struct usb_hci_out_fifo_s { + uint8_t data[4096]; + int len; + } outcmd, outacl, outsco; +}; + +#define USB_EVT_EP 1 +#define USB_ACL_EP 2 +#define USB_SCO_EP 3 + +static const uint8_t qemu_bt_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + USB_DT_DEVICE, /* u8 bDescriptorType; Device */ + 0x10, 0x01, /* u16 bcdUSB; v1.10 */ + + 0xe0, /* u8 bDeviceClass; Wireless */ + 0x01, /* u8 bDeviceSubClass; Radio Frequency */ + 0x01, /* u8 bDeviceProtocol; Bluetooth */ + 0x40, /* u8 bMaxPacketSize0; 64 Bytes */ + + 0x12, 0x0a, /* u16 idVendor; */ + 0x01, 0x00, /* u16 idProduct; Bluetooth Dongle (HCI mode) */ + 0x58, 0x19, /* u16 bcdDevice; (some devices have 0x48, 0x02) */ + + 0x00, /* u8 iManufacturer; */ + 0x00, /* u8 iProduct; */ + 0x00, /* u8 iSerialNumber; */ + 0x01, /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_bt_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + USB_DT_CONFIG, /* u8 bDescriptorType; */ + 0xb1, 0x00, /* u16 wTotalLength; */ + 0x02, /* u8 bNumInterfaces; (2) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* interface one */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x03, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_EVT_EP, /* u8 ep_bEndpointAddress; */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x10, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x02, /* u8 ep_bInterval; */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint three */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting one */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting two */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x01, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting three */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x02, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting four */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x03, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting five */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x04, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* interface two setting six */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x05, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0xe0, /* u8 if_bInterfaceClass; Wireless */ + 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ + 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ + 0x00, /* u8 if_iInterface; */ + + /* endpoint one */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* endpoint two */ + 0x07, /* u8 ep_bLength; */ + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ + USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ + 0x01, /* u8 ep_bmAttributes; Isochronous */ + 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + + /* If implemented, the DFU interface descriptor goes here with no + * endpoints or alternative settings. */ +}; + +static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) +{ + fifo->dstart = 0; + fifo->dlen = 0; + fifo->dsize = DFIFO_LEN_MASK + 1; + fifo->start = 0; + fifo->len = 0; +} + +static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo, + const uint8_t *data, int len) +{ + int off = fifo->dstart + fifo->dlen; + uint8_t *buf; + + fifo->dlen += len; + if (off <= DFIFO_LEN_MASK) { + if (off + len > DFIFO_LEN_MASK + 1 && + (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) { + fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); + exit(-1); + } + buf = fifo->data + off; + } else { + if (fifo->dlen > fifo->dsize) { + fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); + exit(-1); + } + buf = fifo->data + off - fifo->dsize; + } + + off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK; + fifo->fifo[off].data = memcpy(buf, data, len); + fifo->fifo[off].len = len; +} + +static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, + USBPacket *p) +{ + int len; + + if (likely(!fifo->len)) + return USB_RET_STALL; + + len = MIN(p->len, fifo->fifo[fifo->start].len); + memcpy(p->data, fifo->fifo[fifo->start].data, len); + if (len == p->len) { + fifo->fifo[fifo->start].len -= len; + fifo->fifo[fifo->start].data += len; + } else { + fifo->start ++; + fifo->start &= CFIFO_LEN_MASK; + fifo->len --; + } + + fifo->dstart += len; + fifo->dlen -= len; + if (fifo->dstart >= fifo->dsize) { + fifo->dstart = 0; + fifo->dsize = DFIFO_LEN_MASK + 1; + } + + return len; +} + +static void inline usb_bt_fifo_out_enqueue(struct USBBtState *s, + struct usb_hci_out_fifo_s *fifo, + void (*send)(struct HCIInfo *, const uint8_t *, int), + int (*complete)(const uint8_t *, int), + const uint8_t *data, int len) +{ + if (fifo->len) { + memcpy(fifo->data + fifo->len, data, len); + fifo->len += len; + if (complete(fifo->data, fifo->len)) { + send(s->hci, fifo->data, fifo->len); + fifo->len = 0; + } + } else if (complete(data, len)) + send(s->hci, data, len); + else { + memcpy(fifo->data, data, len); + fifo->len = len; + } + + /* TODO: do we need to loop? */ +} + +static int usb_bt_hci_cmd_complete(const uint8_t *data, int len) +{ + len -= HCI_COMMAND_HDR_SIZE; + return len >= 0 && + len >= ((struct hci_command_hdr *) data)->plen; +} + +static int usb_bt_hci_acl_complete(const uint8_t *data, int len) +{ + len -= HCI_ACL_HDR_SIZE; + return len >= 0 && + len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen); +} + +static int usb_bt_hci_sco_complete(const uint8_t *data, int len) +{ + len -= HCI_SCO_HDR_SIZE; + return len >= 0 && + len >= ((struct hci_sco_hdr *) data)->dlen; +} + +static void usb_bt_handle_reset(USBDevice *dev) +{ + struct USBBtState *s = (struct USBBtState *) dev->opaque; + + usb_bt_fifo_reset(&s->evt); + usb_bt_fifo_reset(&s->acl); + usb_bt_fifo_reset(&s->sco); + s->outcmd.len = 0; + s->outacl.len = 0; + s->outsco.len = 0; + s->altsetting = 0; +} + +static int usb_bt_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + struct USBBtState *s = (struct USBBtState *) dev->opaque; + int ret = 0; + + switch (request) { + case DeviceRequest | USB_REQ_GET_STATUS: + case InterfaceRequest | USB_REQ_GET_STATUS: + case EndpointRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + case InterfaceOutRequest | USB_REQ_SET_FEATURE: + case EndpointOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (value >> 8) { + case USB_DT_DEVICE: + ret = sizeof(qemu_bt_dev_descriptor); + memcpy(data, qemu_bt_dev_descriptor, ret); + break; + case USB_DT_CONFIG: + ret = sizeof(qemu_bt_config_descriptor); + memcpy(data, qemu_bt_config_descriptor, ret); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = qemu_bt_config_descriptor[0x5]; + ret = 1; + s->config = 0; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + if (value != qemu_bt_config_descriptor[0x5] && value != 0) { + printf("%s: Wrong SET_CONFIGURATION request (%i)\n", + __FUNCTION__, value); + goto fail; + } + s->config = 1; + usb_bt_fifo_reset(&s->evt); + usb_bt_fifo_reset(&s->acl); + usb_bt_fifo_reset(&s->sco); + break; + case InterfaceRequest | USB_REQ_GET_INTERFACE: + if (value != 0 || (index & ~1) || length != 1) + goto fail; + if (index == 1) + data[0] = s->altsetting; + else + data[0] = 0; + ret = 1; + break; + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + if ((index & ~1) || length != 0 || + (index == 1 && (value < 0 || value > 4)) || + (index == 0 && value != 0)) { + printf("%s: Wrong SET_INTERFACE request (%i, %i)\n", + __FUNCTION__, index, value); + goto fail; + } + s->altsetting = value; + ret = 0; + break; + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): + if (s->config) + usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, + usb_bt_hci_cmd_complete, data, length); + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) +{ + struct USBBtState *s = (struct USBBtState *) dev->opaque; + int ret = 0; + + if (!s->config) + goto fail; + + switch (p->pid) { + case USB_TOKEN_IN: + switch (p->devep & 0xf) { + case USB_EVT_EP: + ret = usb_bt_fifo_dequeue(&s->evt, p); + break; + + case USB_ACL_EP: + ret = usb_bt_fifo_dequeue(&s->acl, p); + break; + + case USB_SCO_EP: + ret = usb_bt_fifo_dequeue(&s->sco, p); + break; + + default: + goto fail; + } + break; + + case USB_TOKEN_OUT: + switch (p->devep & 0xf) { + case USB_ACL_EP: + usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, + usb_bt_hci_acl_complete, p->data, p->len); + break; + + case USB_SCO_EP: + usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, + usb_bt_hci_sco_complete, p->data, p->len); + break; + + default: + goto fail; + } + break; + + default: + fail: + ret = USB_RET_STALL; + break; + } + + return ret; +} + +static void usb_bt_out_hci_packet_event(void *opaque, + const uint8_t *data, int len) +{ + struct USBBtState *s = (struct USBBtState *) opaque; + + usb_bt_fifo_enqueue(&s->evt, data, len); +} + +static void usb_bt_out_hci_packet_acl(void *opaque, + const uint8_t *data, int len) +{ + struct USBBtState *s = (struct USBBtState *) opaque; + + usb_bt_fifo_enqueue(&s->acl, data, len); +} + +static void usb_bt_handle_destroy(USBDevice *dev) +{ + struct USBBtState *s = (struct USBBtState *) dev->opaque; + + s->hci->opaque = 0; + s->hci->evt_recv = 0; + s->hci->acl_recv = 0; + qemu_free(s); +} + +USBDevice *usb_bt_init(HCIInfo *hci) +{ + struct USBBtState *s; + + s = qemu_mallocz(sizeof(struct USBBtState)); + if (!s) + return NULL; + s->dev.opaque = s; + s->dev.speed = USB_SPEED_HIGH; + s->dev.handle_packet = usb_generic_handle_packet; + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU BT dongle"); + + s->dev.handle_reset = usb_bt_handle_reset; + s->dev.handle_control = usb_bt_handle_control; + s->dev.handle_data = usb_bt_handle_data; + s->dev.handle_destroy = usb_bt_handle_destroy; + + s->hci = hci; + s->hci->opaque = s; + s->hci->evt_recv = usb_bt_out_hci_packet_event; + s->hci->acl_recv = usb_bt_out_hci_packet_acl; + + usb_bt_handle_reset(&s->dev); + + return &s->dev; +} diff -Nur kvm-76/qemu/hw/usb-hid.c kvm-userspace/qemu/hw/usb-hid.c --- kvm-76/qemu/hw/usb-hid.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/usb-hid.c 2008-10-12 09:38:24.000000000 +0200 @@ -67,6 +67,8 @@ int protocol; int idle; int changed; + void *datain_opaque; + void (*datain)(void *); } USBHIDState; /* mostly the same values as the Bochs USB Mouse device */ @@ -402,6 +404,14 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static void usb_hid_changed(USBHIDState *hs) +{ + hs->changed = 1; + + if (hs->datain) + hs->datain(hs->datain_opaque); +} + static void usb_mouse_event(void *opaque, int dx1, int dy1, int dz1, int buttons_state) { @@ -412,7 +422,8 @@ s->dy += dy1; s->dz += dz1; s->buttons_state = buttons_state; - hs->changed = 1; + + usb_hid_changed(hs); } static void usb_tablet_event(void *opaque, @@ -425,7 +436,8 @@ s->y = y; s->dz += dz; s->buttons_state = buttons_state; - hs->changed = 1; + + usb_hid_changed(hs); } static void usb_keyboard_event(void *opaque, int keycode) @@ -439,8 +451,6 @@ hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; s->modifiers &= ~(1 << 8); - hs->changed = 1; - switch (hid_code) { case 0x00: return; @@ -465,15 +475,23 @@ if (s->key[i] == hid_code) { s->key[i] = s->key[-- s->keys]; s->key[s->keys] = 0x00; - return; + usb_hid_changed(hs); + break; } + if (i < 0) + return; } else { for (i = s->keys - 1; i >= 0; i --) if (s->key[i] == hid_code) - return; - if (s->keys < sizeof(s->key)) - s->key[s->keys ++] = hid_code; + break; + if (i < 0) { + if (s->keys < sizeof(s->key)) + s->key[s->keys ++] = hid_code; + } else + return; } + + usb_hid_changed(hs); } static inline int int_clamp(int val, int vmin, int vmax) @@ -894,3 +912,11 @@ return (USBDevice *) s; } + +void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) +{ + USBHIDState *s = (USBHIDState *)dev; + + s->datain_opaque = opaque; + s->datain = datain; +} diff -Nur kvm-76/qemu/hw/usb.h kvm-userspace/qemu/hw/usb.h --- kvm-76/qemu/hw/usb.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/usb.h 2008-10-12 09:38:24.000000000 +0200 @@ -247,6 +247,7 @@ USBDevice *usb_mouse_init(void); USBDevice *usb_tablet_init(void); USBDevice *usb_keyboard_init(void); +void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)); /* usb-msd.c */ USBDevice *usb_msd_init(const char *filename); @@ -254,6 +255,9 @@ /* usb-net.c */ USBDevice *usb_net_init(NICInfo *nd); +/* usb-bt.c */ +USBDevice *usb_bt_init(HCIInfo *hci); + /* usb-wacom.c */ USBDevice *usb_wacom_init(void); diff -Nur kvm-76/qemu/hw/versatilepb.c kvm-userspace/qemu/hw/versatilepb.c --- kvm-76/qemu/hw/versatilepb.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/versatilepb.c 2008-10-12 09:38:24.000000000 +0200 @@ -317,13 +317,15 @@ } QEMUMachine versatilepb_machine = { - "versatilepb", - "ARM Versatile/PB (ARM926EJ-S)", - vpb_init, + .name = "versatilepb", + .desc = "ARM Versatile/PB (ARM926EJ-S)", + .init = vpb_init, + .use_scsi = 1, }; QEMUMachine versatileab_machine = { - "versatileab", - "ARM Versatile/AB (ARM926EJ-S)", - vab_init, + .name = "versatileab", + .desc = "ARM Versatile/AB (ARM926EJ-S)", + .init = vab_init, + .use_scsi = 1, }; diff -Nur kvm-76/qemu/hw/vga.c kvm-userspace/qemu/hw/vga.c --- kvm-76/qemu/hw/vga.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/vga.c 2008-10-12 09:38:24.000000000 +0200 @@ -27,6 +27,7 @@ #include "pci.h" #include "vga_int.h" #include "pixel_ops.h" +#include "qemu-timer.h" #include "qemu-kvm.h" #include @@ -152,6 +153,138 @@ static void vga_screen_dump(void *opaque, const char *filename); +static void vga_dumb_update_retrace_info(VGAState *s) +{ + (void) s; +} + +static void vga_precise_update_retrace_info(VGAState *s) +{ + int htotal_chars; + int hretr_start_char; + int hretr_skew_chars; + int hretr_end_char; + + int vtotal_lines; + int vretr_start_line; + int vretr_end_line; + + int div2, sldiv2, dots; + int clocking_mode; + int clock_sel; + const int hz[] = {25175000, 28322000, 25175000, 25175000}; + int64_t chars_per_sec; + struct vga_precise_retrace *r = &s->retrace_info.precise; + + htotal_chars = s->cr[0x00] + 5; + hretr_start_char = s->cr[0x04]; + hretr_skew_chars = (s->cr[0x05] >> 5) & 3; + hretr_end_char = s->cr[0x05] & 0x1f; + + vtotal_lines = (s->cr[0x06] + | (((s->cr[0x07] & 1) | ((s->cr[0x07] >> 4) & 2)) << 8)) + 2 + ; + vretr_start_line = s->cr[0x10] + | ((((s->cr[0x07] >> 2) & 1) | ((s->cr[0x07] >> 6) & 2)) << 8) + ; + vretr_end_line = s->cr[0x11] & 0xf; + + + div2 = (s->cr[0x17] >> 2) & 1; + sldiv2 = (s->cr[0x17] >> 3) & 1; + + clocking_mode = (s->sr[0x01] >> 3) & 1; + clock_sel = (s->msr >> 2) & 3; + dots = (s->msr & 1) ? 8 : 9; + + chars_per_sec = hz[clock_sel] / dots; + + htotal_chars <<= clocking_mode; + + r->total_chars = vtotal_lines * htotal_chars; + r->total_chars = (vretr_start_line + vretr_end_line + 1) * htotal_chars; + if (r->freq) { + r->ticks_per_char = ticks_per_sec / (r->total_chars * r->freq); + } else { + r->ticks_per_char = ticks_per_sec / chars_per_sec; + } + + r->vstart = vretr_start_line; + r->vend = r->vstart + vretr_end_line + 1; + + r->hstart = hretr_start_char + hretr_skew_chars; + r->hend = r->hstart + hretr_end_char + 1; + r->htotal = htotal_chars; + +#if 0 + printf("hz=%f\n", + printf ( + "hz=%f\n" + "htotal = %d\n" + "hretr_start = %d\n" + "hretr_skew = %d\n" + "hretr_end = %d\n" + "vtotal = %d\n" + "vretr_start = %d\n" + "vretr_end = %d\n" + "div2 = %d sldiv2 = %d\n" + "clocking_mode = %d\n" + "clock_sel = %d %d\n" + "dots = %d\n" + "ticks/char = %lld\n" + "\n", + (double) ticks_per_sec / (r->ticks_per_char * r->total_chars), + htotal_chars, + hretr_start_char, + hretr_skew_chars, + hretr_end_char, + vtotal_lines, + vretr_start_line, + vretr_end_line, + div2, sldiv2, + clocking_mode, + clock_sel, + hz[clock_sel], + dots, + r->ticks_per_char + ); +#endif +} + +static uint8_t vga_precise_retrace(VGAState *s) +{ + struct vga_precise_retrace *r = &s->retrace_info.precise; + uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE); + + if (r->total_chars) { + int cur_line, cur_line_char, cur_char; + int64_t cur_tick; + + cur_tick = qemu_get_clock(vm_clock); + + cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; + cur_line = cur_char / r->htotal; + + if (cur_line >= r->vstart && cur_line <= r->vend) { + val |= ST01_V_RETRACE | ST01_DISP_ENABLE; + } else { + cur_line_char = cur_char % r->htotal; + if (cur_line_char >= r->hstart && cur_line_char <= r->hend) { + val |= ST01_DISP_ENABLE; + } + } + + return val; + } else { + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); + } +} + +static uint8_t vga_dumb_retrace(VGAState *s) +{ + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); +} + static uint32_t vga_ioport_read(void *opaque, uint32_t addr) { VGAState *s = opaque; @@ -231,8 +364,7 @@ case 0x3ba: case 0x3da: /* just toggle to fool polling */ - s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE; - val = s->st01; + val = s->st01 = s->retrace(s); s->ar_flip_flop = 0; break; default: @@ -294,6 +426,7 @@ break; case 0x3c2: s->msr = val & ~0x10; + s->update_retrace_info(s); break; case 0x3c4: s->sr_index = val & 7; @@ -303,6 +436,7 @@ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); #endif s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + if (s->sr_index == 1) s->update_retrace_info(s); break; case 0x3c7: s->dac_read_index = val; @@ -360,6 +494,18 @@ s->cr[s->cr_index] = val; break; } + + switch(s->cr_index) { + case 0x00: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x11: + case 0x17: + s->update_retrace_info(s); + break; + } break; case 0x3ba: case 0x3da: @@ -1983,36 +2129,6 @@ VGAState vga_state; } PCIVGAState; -void vga_update_vram_mapping(VGAState *s, unsigned long vga_ram_begin, - unsigned long vga_ram_end) -{ - void *vram_pointer, *old_vram; - - if (vga_ram_begin == s->map_addr && - vga_ram_end == s->map_end) { - return; - } - - if (s->map_addr && s->map_end) - unset_vram_mapping(s->map_addr, s->map_end); - - vram_pointer = set_vram_mapping(vga_ram_begin, vga_ram_end); - if (!vram_pointer) { - fprintf(stderr, "set_vram_mapping failed\n"); - s->map_addr = s->map_end = 0; - } - else { - old_vram = vga_update_vram((VGAState *)s, vram_pointer, - VGA_RAM_SIZE); - if (s->map_addr && s->map_end) - munmap(old_vram, s->map_end - s->map_addr); - else - qemu_free(old_vram); - s->map_addr = vga_ram_begin; - s->map_end = vga_ram_end; - } -} - static void vga_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { @@ -2022,8 +2138,10 @@ cpu_register_physical_memory(addr, s->bios_size, s->bios_offset); } else { cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); - if (kvm_enabled()) - vga_update_vram_mapping(s, addr, addr + VGA_RAM_SIZE); + if (kvm_enabled()) { + kvm_qemu_log_memory(addr, VGA_RAM_SIZE, 1); + s->map_addr = addr; + } } } @@ -2190,10 +2308,7 @@ vga_reset(s); - if (kvm_enabled()) - s->vram_ptr = qemu_malloc(vga_ram_size); - else - s->vram_ptr = vga_ram_base; + s->vram_ptr = vga_ram_base; s->vram_offset = vga_ram_offset; s->vram_size = vga_ram_size; s->ds = ds; @@ -2204,6 +2319,18 @@ s->invalidate = vga_invalidate_display; s->screen_dump = vga_screen_dump; s->text_update = vga_update_text; + switch (vga_retrace_method) { + case VGA_RETRACE_DUMB: + s->retrace = vga_dumb_retrace; + s->update_retrace_info = vga_dumb_update_retrace_info; + break; + + case VGA_RETRACE_PRECISE: + s->retrace = vga_precise_retrace; + s->update_retrace_info = vga_precise_update_retrace_info; + memset(&s->retrace_info, 0, sizeof (s->retrace_info)); + break; + } #ifdef TARGET_IA64 vga_bios_init(s); #endif diff -Nur kvm-76/qemu/hw/vga_int.h kvm-userspace/qemu/hw/vga_int.h --- kvm-76/qemu/hw/vga_int.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/hw/vga_int.h 2008-10-12 09:38:24.000000000 +0200 @@ -79,6 +79,25 @@ #define CH_ATTR_SIZE (160 * 100) #define VGA_MAX_HEIGHT 2048 +struct vga_precise_retrace { + int64_t ticks_per_char; + int64_t total_chars; + int htotal; + int hstart; + int hend; + int vstart; + int vend; + int freq; +}; + +union vga_retrace { + struct vga_precise_retrace precise; +}; + +struct VGAState; +typedef uint8_t (* vga_retrace_fn)(struct VGAState *s); +typedef void (* vga_update_retrace_info_fn)(struct VGAState *s); + #define VGA_STATE_COMMON \ uint8_t *vram_ptr; \ unsigned long vram_offset; \ @@ -147,13 +166,18 @@ void (*cursor_draw_line)(struct VGAState *s, uint8_t *d, int y); \ /* tell for each page if it has been updated since the last time */ \ uint32_t last_palette[256]; \ - uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ \ + uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ \ + /* kvm */ \ unsigned long map_addr; \ unsigned long map_end; \ int32_t aliases_enabled; \ int32_t pad1; \ uint32_t aliased_bank_base[2]; \ - uint32_t aliased_bank_limit[2]; + uint32_t aliased_bank_limit[2]; \ + /* retrace */ \ + vga_retrace_fn retrace; \ + vga_update_retrace_info_fn update_retrace_info; \ + union vga_retrace retrace_info; typedef struct VGAState { @@ -191,8 +215,8 @@ unsigned int color_xor); /* let kvm create vga memory */ -void *set_vram_mapping(unsigned long begin, unsigned long end); -int unset_vram_mapping(unsigned long begin, unsigned long end); +void set_vram_mapping(void *mem, unsigned long begin, unsigned long end); +void unset_vram_mapping(void *mem, unsigned long begin, unsigned long end); void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size); void vga_update_vram_mapping(VGAState *s, unsigned long vga_ram_begin, diff -Nur kvm-76/qemu/i386-dis.c kvm-userspace/qemu/i386-dis.c --- kvm-76/qemu/i386-dis.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/i386-dis.c 2008-10-12 09:38:24.000000000 +0200 @@ -1131,63 +1131,63 @@ need to update onebyte_has_modrm or twobyte_has_modrm. */ #define MODRM_CHECK if (!need_modrm) abort () -static const char **names64; -static const char **names32; -static const char **names16; -static const char **names8; -static const char **names8rex; -static const char **names_seg; -static const char **index16; +static const char * const *names64; +static const char * const *names32; +static const char * const *names16; +static const char * const *names8; +static const char * const *names8rex; +static const char * const *names_seg; +static const char * const *index16; -static const char *intel_names64[] = { +static const char * const intel_names64[] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; -static const char *intel_names32[] = { +static const char * const intel_names32[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }; -static const char *intel_names16[] = { +static const char * const intel_names16[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }; -static const char *intel_names8[] = { +static const char * const intel_names8[] = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", }; -static const char *intel_names8rex[] = { +static const char * const intel_names8rex[] = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }; -static const char *intel_names_seg[] = { +static const char * const intel_names_seg[] = { "es", "cs", "ss", "ds", "fs", "gs", "?", "?", }; -static const char *intel_index16[] = { +static const char * const intel_index16[] = { "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" }; -static const char *att_names64[] = { +static const char * const att_names64[] = { "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" }; -static const char *att_names32[] = { +static const char * const att_names32[] = { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" }; -static const char *att_names16[] = { +static const char * const att_names16[] = { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" }; -static const char *att_names8[] = { +static const char * const att_names8[] = { "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", }; -static const char *att_names8rex[] = { +static const char * const att_names8rex[] = { "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" }; -static const char *att_names_seg[] = { +static const char * const att_names_seg[] = { "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?", }; -static const char *att_index16[] = { +static const char * const att_index16[] = { "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" }; diff -Nur kvm-76/qemu/keymaps.c kvm-userspace/qemu/keymaps.c --- kvm-76/qemu/keymaps.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/keymaps.c 2008-10-12 09:38:24.000000000 +0200 @@ -24,7 +24,7 @@ static int get_keysym(const char *name) { - name2keysym_t *p; + const name2keysym_t *p; for(p = name2keysym; p->name != NULL; p++) { if (!strcmp(p->name, name)) return p->keysym; diff -Nur kvm-76/qemu/kvm-compatfd.c kvm-userspace/qemu/kvm-compatfd.c --- kvm-76/qemu/kvm-compatfd.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/kvm-compatfd.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,33 +0,0 @@ -/* - * signalfd/eventfd compatibility - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu-common.h" -#include "qemu-kvm.h" - -#include - -int kvm_eventfd(int *fds) -{ -#if defined(SYS_eventfd) - int ret; - - ret = syscall(SYS_eventfd, 0); - if (ret >= 0) { - fds[0] = fds[1] = ret; - return 0; - } else if (!(ret == -1 && errno == ENOSYS)) - return ret; -#endif - - return pipe(fds); -} diff -Nur kvm-76/qemu/linux-user/elfload.c kvm-userspace/qemu/linux-user/elfload.c --- kvm-76/qemu/linux-user/elfload.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/elfload.c 2008-10-12 09:38:24.000000000 +0200 @@ -1148,6 +1148,7 @@ end_code = 0; start_data = 0; end_data = 0; + interp_ex.a_info = 0; for(i=0;i < elf_ex.e_phnum; i++) { if (elf_ppnt->p_type == PT_INTERP) { diff -Nur kvm-76/qemu/linux-user/main.c kvm-userspace/qemu/linux-user/main.c --- kvm-76/qemu/linux-user/main.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/main.c 2008-10-12 09:38:24.000000000 +0200 @@ -2253,7 +2253,7 @@ break; } else if (!strcmp(r, "d")) { int mask; - CPULogItem *item; + const CPULogItem *item; if (optind >= argc) break; diff -Nur kvm-76/qemu/linux-user/mmap.c kvm-userspace/qemu/linux-user/mmap.c --- kvm-76/qemu/linux-user/mmap.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/mmap.c 2008-10-12 09:38:24.000000000 +0200 @@ -26,6 +26,7 @@ #include #include "qemu.h" +#include "qemu-common.h" //#define DEBUG_MMAP diff -Nur kvm-76/qemu/linux-user/strace.c kvm-userspace/qemu/linux-user/strace.c --- kvm-76/qemu/linux-user/strace.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/strace.c 2008-10-12 09:38:24.000000000 +0200 @@ -154,6 +154,7 @@ } #endif +#ifdef TARGET_NR_semctl static void print_semctl(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, @@ -163,6 +164,7 @@ print_ipc_cmd(arg3); gemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); } +#endif static void print_execve(const struct syscallname *name, @@ -178,7 +180,7 @@ unlock_user(s, arg1, 0); for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) { - abi_ulong *arg_ptr, arg_addr, s_addr; + abi_ulong *arg_ptr, arg_addr; arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); if (!arg_ptr) @@ -189,7 +191,7 @@ break; if ((s = lock_user_string(arg_addr))) { gemu_log("\"%s\",", s); - unlock_user(s, s_addr, 0); + unlock_user(s, arg_addr, 0); } } diff -Nur kvm-76/qemu/linux-user/syscall.c kvm-userspace/qemu/linux-user/syscall.c --- kvm-76/qemu/linux-user/syscall.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/syscall.c 2008-10-12 09:38:24.000000000 +0200 @@ -177,6 +177,9 @@ #define __NR_sys_unlinkat __NR_unlinkat #define __NR_sys_utimensat __NR_utimensat #define __NR_sys_futex __NR_futex +#define __NR_sys_inotify_init __NR_inotify_init +#define __NR_sys_inotify_add_watch __NR_inotify_add_watch +#define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch #if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) #define __NR__llseek __NR_lseek @@ -270,6 +273,15 @@ _syscall4(int,sys_utimensat,int,dirfd,const char *,pathname, const struct timespec *,tsp,int,flags) #endif +#if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) +_syscall0(int,sys_inotify_init) +#endif +#if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch) +_syscall3(int,sys_inotify_add_watch,int,fd,const char *,pathname,uint32_t,mask) +#endif +#if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch) +_syscall2(int,sys_inotify_rm_watch,int,fd,uint32_t,wd) +#endif #if defined(USE_NPTL) #if defined(TARGET_NR_futex) && defined(__NR_futex) _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, @@ -2127,7 +2139,7 @@ #define MAX_STRUCT_SIZE 4096 -IOCTLEntry ioctl_entries[] = { +static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, types...) \ { TARGET_ ## cmd, cmd, #cmd, access, { types } }, #include "ioctls.h" @@ -2218,7 +2230,7 @@ return ret; } -bitmask_transtbl iflag_tbl[] = { +static const bitmask_transtbl iflag_tbl[] = { { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK }, { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT }, { TARGET_IGNPAR, TARGET_IGNPAR, IGNPAR, IGNPAR }, @@ -2236,7 +2248,7 @@ { 0, 0, 0, 0 } }; -bitmask_transtbl oflag_tbl[] = { +static const bitmask_transtbl oflag_tbl[] = { { TARGET_OPOST, TARGET_OPOST, OPOST, OPOST }, { TARGET_OLCUC, TARGET_OLCUC, OLCUC, OLCUC }, { TARGET_ONLCR, TARGET_ONLCR, ONLCR, ONLCR }, @@ -2264,7 +2276,7 @@ { 0, 0, 0, 0 } }; -bitmask_transtbl cflag_tbl[] = { +static const bitmask_transtbl cflag_tbl[] = { { TARGET_CBAUD, TARGET_B0, CBAUD, B0 }, { TARGET_CBAUD, TARGET_B50, CBAUD, B50 }, { TARGET_CBAUD, TARGET_B75, CBAUD, B75 }, @@ -2299,7 +2311,7 @@ { 0, 0, 0, 0 } }; -bitmask_transtbl lflag_tbl[] = { +static const bitmask_transtbl lflag_tbl[] = { { TARGET_ISIG, TARGET_ISIG, ISIG, ISIG }, { TARGET_ICANON, TARGET_ICANON, ICANON, ICANON }, { TARGET_XCASE, TARGET_XCASE, XCASE, XCASE }, @@ -2386,7 +2398,7 @@ target->c_cc[TARGET_VEOL2] = host->c_cc[VEOL2]; } -StructEntry struct_termios_def = { +static const StructEntry struct_termios_def = { .convert = { host_to_target_termios, target_to_host_termios }, .size = { sizeof(struct target_termios), sizeof(struct host_termios) }, .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) }, @@ -5576,7 +5588,40 @@ goto unimplemented; #ifdef TARGET_NR_mincore case TARGET_NR_mincore: - goto unimplemented; + { + void *a; + ret = -TARGET_EFAULT; + if (!(a = lock_user(VERIFY_READ, arg1,arg2, 0))) + goto efault; + if (!(p = lock_user_string(arg3))) + goto mincore_fail; + ret = get_errno(mincore(a, arg2, p)); + unlock_user(p, arg3, ret); + mincore_fail: + unlock_user(a, arg1, 0); + } + break; +#endif +#ifdef TARGET_NR_arm_fadvise64_64 + case TARGET_NR_arm_fadvise64_64: + { + /* + * arm_fadvise64_64 looks like fadvise64_64 but + * with different argument order + */ + abi_long temp; + temp = arg3; + arg3 = arg4; + arg4 = temp; + } +#endif +#if defined(TARGET_NR_fadvise64_64) || defined(TARGET_NR_arm_fadvise64_64) +#ifdef TARGET_NR_fadvise64_64 + case TARGET_NR_fadvise64_64: +#endif + /* This is a hint, so ignoring and returning success is ok. */ + ret = get_errno(0); + break; #endif #ifdef TARGET_NR_madvise case TARGET_NR_madvise: @@ -5841,6 +5886,23 @@ ret = do_futex(arg1, arg2, arg3, arg4, arg5, arg6); break; #endif +#ifdef TARGET_NR_inotify_init + case TARGET_NR_inotify_init: + ret = get_errno(sys_inotify_init()); + break; +#endif +#ifdef TARGET_NR_inotify_add_watch + case TARGET_NR_inotify_add_watch: + p = lock_user_string(arg2); + ret = get_errno(sys_inotify_add_watch(arg1, path(p), arg3)); + unlock_user(p, arg2, 0); + break; +#endif +#ifdef TARGET_NR_inotify_rm_watch + case TARGET_NR_inotify_rm_watch: + ret = get_errno(sys_inotify_rm_watch(arg1, arg2)); + break; +#endif default: unimplemented: diff -Nur kvm-76/qemu/linux-user/syscall_defs.h kvm-userspace/qemu/linux-user/syscall_defs.h --- kvm-76/qemu/linux-user/syscall_defs.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/linux-user/syscall_defs.h 2008-10-12 09:38:24.000000000 +0200 @@ -860,11 +860,14 @@ #include "termbits.h" +/* Common */ #define TARGET_MAP_SHARED 0x01 /* Share changes */ #define TARGET_MAP_PRIVATE 0x02 /* Changes are private */ -#define TARGET_MAP_TYPE 0x0f /* Mask for type of mapping */ -#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ +#define TARGET_MAP_TYPE 0x0f /* Mask for type of mapping */ + +/* Target specific */ #if defined(TARGET_MIPS) +#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ #define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */ #define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */ #define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */ @@ -873,18 +876,34 @@ #define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */ #define TARGET_MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ #define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */ -#else +#elif defined(TARGET_PPC) +#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ #define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ #define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */ #define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -#if defined(TARGET_PPC) #define TARGET_MAP_LOCKED 0x0080 /* pages are locked */ #define TARGET_MAP_NORESERVE 0x0040 /* don't check for reservations */ +#define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ +#elif defined(TARGET_ALPHA) +#define TARGET_MAP_ANONYMOUS 0x10 /* don't use a file */ +#define TARGET_MAP_FIXED 0x100 /* Interpret addr exactly */ +#define TARGET_MAP_GROWSDOWN 0x01000 /* stack-like segment */ +#define TARGET_MAP_DENYWRITE 0x02000 /* ETXTBSY */ +#define TARGET_MAP_EXECUTABLE 0x04000 /* mark it as an executable */ +#define TARGET_MAP_LOCKED 0x08000 /* lock the mapping */ +#define TARGET_MAP_NORESERVE 0x10000 /* no check for reservations */ +#define TARGET_MAP_POPULATE 0x20000 /* pop (prefault) pagetables */ +#define TARGET_MAP_NONBLOCK 0x40000 /* do not block on IO */ #else +#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ +#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ +#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */ +#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define TARGET_MAP_LOCKED 0x2000 /* pages are locked */ #define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */ -#endif #define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ #endif diff -Nur kvm-76/qemu/migration.c kvm-userspace/qemu/migration.c --- kvm-76/qemu/migration.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/migration.c 2008-10-12 09:38:24.000000000 +0200 @@ -154,7 +154,7 @@ } } -static void migrate_close(void *opaque) +static int migrate_close(void *opaque) { MigrationState *s = opaque; @@ -163,6 +163,7 @@ qemu_free(s); current_migration = NULL; + return 0; } /* Outgoing migration routines */ @@ -178,7 +179,7 @@ if (! *has_error) { vm_stop(0); - f = qemu_fopen(s, migrate_put_buffer, NULL, migrate_close); + f = qemu_fopen_ops(s, migrate_put_buffer, NULL, migrate_close, NULL); do { qemu_aio_flush(); } while (qemu_bh_poll()); @@ -823,6 +824,7 @@ uint32_t addr, size; extern void qemu_announce_self(void); unsigned char running; + extern int autostart; running = qemu_get_byte(f); if ((running != 1) && (running != 2)) { diff -Nur kvm-76/qemu/monitor.c kvm-userspace/qemu/monitor.c --- kvm-76/qemu/monitor.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/monitor.c 2008-10-12 09:38:24.000000000 +0200 @@ -70,8 +70,8 @@ static CharDriverState *monitor_hd[MAX_MON]; static int hide_banner; -static term_cmd_t term_cmds[]; -static term_cmd_t info_cmds[]; +static const term_cmd_t term_cmds[]; +static const term_cmd_t info_cmds[]; static uint8_t term_outbuf[1024]; static int term_outbuf_index; @@ -179,9 +179,9 @@ return 0; } -static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name) +static void help_cmd1(const term_cmd_t *cmds, const char *prefix, const char *name) { - term_cmd_t *cmd; + const term_cmd_t *cmd; for(cmd = cmds; cmd->name != NULL; cmd++) { if (!name || !strcmp(name, cmd->name)) @@ -196,7 +196,7 @@ } else { help_cmd1(term_cmds, "", name); if (name && !strcmp(name, "log")) { - CPULogItem *item; + const CPULogItem *item; term_printf("Log items (comma separated):\n"); term_printf("%-10s %s\n", "none", "remove all logs"); for(item = cpu_log_items; item->mask != 0; item++) { @@ -225,7 +225,7 @@ static void do_info(const char *item) { - term_cmd_t *cmd; + const term_cmd_t *cmd; void (*handler)(void); if (!item) @@ -886,6 +886,9 @@ { 0x30, "b" }, { 0x31, "n" }, { 0x32, "m" }, + { 0x33, "comma" }, + { 0x34, "dot" }, + { 0x35, "slash" }, { 0x37, "asterisk" }, @@ -1373,9 +1376,6 @@ } #ifdef HAS_AUDIO -int wav_start_capture (CaptureState *s, const char *path, int freq, - int bits, int nchannels); - static void do_wav_capture (const char *path, int has_freq, int freq, int has_bits, int bits, @@ -1408,7 +1408,10 @@ for (env = first_cpu; env != NULL; env = env->next_cpu) if (env->cpu_index == cpu_index) { - cpu_interrupt(env, CPU_INTERRUPT_NMI); + if (kvm_enabled()) + kvm_inject_interrupt(env, CPU_INTERRUPT_NMI); + else + cpu_interrupt(env, CPU_INTERRUPT_NMI); break; } } @@ -1433,7 +1436,7 @@ term_printf("balloon: actual=%d\n", (int)(actual >> 20)); } -static term_cmd_t term_cmds[] = { +static const term_cmd_t term_cmds[] = { { "help|?", "s?", do_help, "[cmd]", "show the help" }, { "commit", "s", do_commit, @@ -1533,7 +1536,7 @@ { NULL, NULL, }, }; -static term_cmd_t info_cmds[] = { +static const term_cmd_t info_cmds[] = { { "version", "", do_info_version, "", "show the version of qemu" }, { "network", "", do_info_network, @@ -1612,12 +1615,12 @@ typedef struct MonitorDef { const char *name; int offset; - target_long (*get_value)(struct MonitorDef *md, int val); + target_long (*get_value)(const struct MonitorDef *md, int val); int type; } MonitorDef; #if defined(TARGET_I386) -static target_long monitor_get_pc (struct MonitorDef *md, int val) +static target_long monitor_get_pc (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1627,7 +1630,7 @@ #endif #if defined(TARGET_PPC) -static target_long monitor_get_ccr (struct MonitorDef *md, int val) +static target_long monitor_get_ccr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); unsigned int u; @@ -1643,7 +1646,7 @@ return u; } -static target_long monitor_get_msr (struct MonitorDef *md, int val) +static target_long monitor_get_msr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1651,7 +1654,7 @@ return env->msr; } -static target_long monitor_get_xer (struct MonitorDef *md, int val) +static target_long monitor_get_xer (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1659,7 +1662,7 @@ return ppc_load_xer(env); } -static target_long monitor_get_decr (struct MonitorDef *md, int val) +static target_long monitor_get_decr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1667,7 +1670,7 @@ return cpu_ppc_load_decr(env); } -static target_long monitor_get_tbu (struct MonitorDef *md, int val) +static target_long monitor_get_tbu (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1675,7 +1678,7 @@ return cpu_ppc_load_tbu(env); } -static target_long monitor_get_tbl (struct MonitorDef *md, int val) +static target_long monitor_get_tbl (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1686,7 +1689,7 @@ #if defined(TARGET_SPARC) #ifndef TARGET_SPARC64 -static target_long monitor_get_psr (struct MonitorDef *md, int val) +static target_long monitor_get_psr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1695,7 +1698,7 @@ } #endif -static target_long monitor_get_reg(struct MonitorDef *md, int val) +static target_long monitor_get_reg(const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); if (!env) @@ -1704,7 +1707,7 @@ } #endif -static MonitorDef monitor_defs[] = { +static const MonitorDef monitor_defs[] = { #ifdef TARGET_I386 #define SEG(name, seg) \ @@ -1955,7 +1958,7 @@ /* return 0 if OK, -1 if not found, -2 if no CPU defined */ static int get_monitor_def(target_long *pval, const char *name) { - MonitorDef *md; + const MonitorDef *md; void *ptr; for(md = monitor_defs; md->name != NULL; md++) { @@ -2249,7 +2252,7 @@ const char *p, *pstart, *typestr; char *q; int c, nb_args, len, i, has_arg; - term_cmd_t *cmd; + const term_cmd_t *cmd; char cmdname[256]; char buf[1024]; void *str_allocated[MAX_ARGS]; @@ -2412,8 +2415,8 @@ /* for 'i', not specifying a size gives -1 as size */ if (size < 0) size = default_fmt_size; + default_fmt_size = size; } - default_fmt_size = size; default_fmt_format = format; } else { count = 1; @@ -2686,7 +2689,7 @@ char *args[MAX_ARGS]; int nb_args, i, len; const char *ptype, *str; - term_cmd_t *cmd; + const term_cmd_t *cmd; const KeyDef *key; parse_cmdline(cmdline, &nb_args, args); diff -Nur kvm-76/qemu/net.h kvm-userspace/qemu/net.h --- kvm-76/qemu/net.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/net.h 2008-10-12 09:38:24.000000000 +0200 @@ -66,6 +66,20 @@ extern int nb_nics; extern NICInfo nd_table[MAX_NICS]; +/* BT HCI info */ + +struct HCIInfo { + int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr); + void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len); + void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len); + void (*acl_send)(struct HCIInfo *hci, const uint8_t *data, int len); + void *opaque; + void (*evt_recv)(void *opaque, const uint8_t *data, int len); + void (*acl_recv)(void *opaque, const uint8_t *data, int len); +}; + +struct HCIInfo *qemu_next_hci(void); + /* checksumming functions (net-checksum.c) */ uint32_t net_checksum_add(int len, uint8_t *buf); uint16_t net_checksum_finish(uint32_t sum); Binärdateien kvm-76/qemu/pc-bios/bios.bin and kvm-userspace/qemu/pc-bios/bios.bin sind verschieden. diff -Nur kvm-76/qemu/qemu-common.h kvm-userspace/qemu/qemu-common.h --- kvm-76/qemu/qemu-common.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-common.h 2008-10-12 09:38:24.000000000 +0200 @@ -119,6 +119,7 @@ /* A load of opaque types so that device init declarations don't have to pull in all the real definitions. */ typedef struct NICInfo NICInfo; +typedef struct HCIInfo HCIInfo; typedef struct AudioState AudioState; typedef struct BlockDriverState BlockDriverState; typedef struct DisplayState DisplayState; diff -Nur kvm-76/qemu/qemu-doc.texi kvm-userspace/qemu/qemu-doc.texi --- kvm-76/qemu/qemu-doc.texi 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-doc.texi 2008-10-12 09:38:24.000000000 +0200 @@ -345,6 +345,9 @@ a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or gigabytes respectively. +@item -cpu @var{model} +Select CPU model (-cpu ? for list and additional feature selection) + @item -smp @var{n} Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255 CPUs are supported. On Sparc32 target, Linux limits the number of usable CPUs @@ -963,11 +966,24 @@ @item -L path Set the directory for the BIOS, VGA BIOS and keymaps. -@item -std-vga -Simulate a standard VGA card with Bochs VBE extensions (default is -Cirrus Logic GD5446 PCI VGA). If your guest OS supports the VESA 2.0 -VBE extensions (e.g. Windows XP) and if you want to use high -resolution modes (>= 1280x1024x16) then you should use this option. +@item -vga @var{type} +Select type of VGA card to emulate. Valid values for @var{type} are +@table @code +@item cirrus +Cirrus Logic GD5446 Video card. All Windows versions starting from +Windows 95 should recognize and use this graphic card. For optimal +performances, use 16 bit color depth in the guest and the host OS. +(This one is the default) +@item std +Standard VGA card with Bochs VBE extensions. If your guest OS +supports the VESA 2.0 VBE extensions (e.g. Windows XP) and if you want +to use high resolution modes (>= 1280x1024x16) then you should use +this option. +@item vmware +VMWare SVGA-II compatible adapter. Use it if you have sufficiently +recent XFree86/XOrg server or Windows guest with a driver for this +card. +@end table @item -no-acpi Disable ACPI (Advanced Configuration and Power Interface) support. Use @@ -1292,8 +1308,9 @@ @item sendkey @var{keys} -Send @var{keys} to the emulator. Use @code{-} to press several keys -simultaneously. Example: +Send @var{keys} to the emulator. @var{keys} could be the name of the +key or @code{#} followed by the raw value in either decimal or hexadecimal +format. Use @code{-} to press several keys simultaneously. Example: @example sendkey ctrl-alt-f1 @end example @@ -1832,7 +1849,7 @@ to provide high security. The password can be fairly easily brute-forced by a client making repeat connections. For this reason, a VNC server using password authentication should be restricted to only listen on the loopback interface -or UNIX domain sockets. Password ayuthentication is requested with the @code{password} +or UNIX domain sockets. Password authentication is requested with the @code{password} option, and then once QEMU is running the password is set with the monitor. Until the monitor is used to set the password all clients will be rejected. @@ -2250,14 +2267,37 @@ @node Sparc32 System emulator @section Sparc32 System emulator -Use the executable @file{qemu-system-sparc} to simulate a SPARCstation -5, SPARCstation 10, SPARCstation 20, SPARCserver 600MP (sun4m -architecture), SPARCstation 2 (sun4c architecture), SPARCserver 1000, -or SPARCcenter 2000 (sun4d architecture). The emulation is somewhat -complete. SMP up to 16 CPUs is supported, but Linux limits the number -of usable CPUs to 4. +Use the executable @file{qemu-system-sparc} to simulate the following +Sun4m architecture machines: +@itemize @minus +@item +SPARCstation 4 +@item +SPARCstation 5 +@item +SPARCstation 10 +@item +SPARCstation 20 +@item +SPARCserver 600MP +@item +SPARCstation LX +@item +SPARCstation Voyager +@item +SPARCclassic +@item +SPARCbook +@end itemize + +The emulation is somewhat complete. SMP up to 16 CPUs is supported, +but Linux limits the number of usable CPUs to 4. + +It's also possible to simulate a SPARCstation 2 (sun4c architecture), +SPARCserver 1000, or SPARCcenter 2000 (sun4d architecture), but these +emulators are not usable yet. -QEMU emulates the following sun4m/sun4d peripherals: +QEMU emulates the following sun4m/sun4c/sun4d peripherals: @itemize @minus @item @@ -2267,7 +2307,7 @@ @item Lance (Am7990) Ethernet @item -Non Volatile RAM M48T08 +Non Volatile RAM M48T02/M48T08 @item Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard and power/reset logic @@ -2289,8 +2329,10 @@ 1275-1994 (referred to as Open Firmware) compliant firmware. A sample Linux 2.6 series kernel and ram disk image are available on -the QEMU web site. Please note that currently NetBSD, OpenBSD or -Solaris kernels don't work. +the QEMU web site. There are still issues with NetBSD and OpenBSD, but +some kernel versions work. Please note that currently Solaris kernels +don't work probably due to interface issues between OpenBIOS and +Solaris. @c man begin OPTIONS @@ -2312,7 +2354,7 @@ -prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single' @end example -@item -M [SS-5|SS-10|SS-20|SS-600MP|SS-2|SS-1000|SS-2000] +@item -M [SS-4|SS-5|SS-10|SS-20|SS-600MP|LX|Voyager|SPARCClassic|SPARCbook|SS-2|SS-1000|SS-2000] Set the emulated machine type. Default is SS-5. @@ -2323,8 +2365,10 @@ @node Sparc64 System emulator @section Sparc64 System emulator -Use the executable @file{qemu-system-sparc64} to simulate a Sun4u or -Sun4v machine. The emulator is not usable for anything yet. +Use the executable @file{qemu-system-sparc64} to simulate a Sun4u +(UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic +Niagara (T1) machine. The emulator is not usable for anything yet, but +it can launch some kernels. QEMU emulates the following peripherals: @@ -2334,11 +2378,15 @@ @item PCI VGA compatible card with VESA Bochs Extensions @item +PS/2 mouse and keyboard +@item Non Volatile RAM M48T59 @item PC-compatible serial ports @item 2 PCI IDE interfaces with hard disk and CD-ROM support +@item +Floppy disk @end itemize @c man begin OPTIONS @@ -2347,7 +2395,15 @@ @table @option -@item -M [sun4u|sun4v] +@item -prom-env string + +Set OpenBIOS variables in NVRAM, for example: + +@example +qemu-system-sparc64 -prom-env 'auto-boot?=false' +@end example + +@item -M [sun4u|sun4v|Niagara] Set the emulated machine type. The default is sun4u. @@ -2804,7 +2860,7 @@ @subsection Command line options @example -usage: qemu-i386 [-h] [-d] [-L path] [-s size] program [arguments...] +usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] program [arguments...] @end example @table @option @@ -2814,6 +2870,8 @@ Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386) @item -s size Set the x86 stack size in bytes (default=524288) +@item -cpu model +Select CPU model (-cpu ? for list and additional feature selection) @end table Debug options: @@ -2823,6 +2881,8 @@ Activate log (logfile=/tmp/qemu.log) @item -p pagesize Act as if the host page size was 'pagesize' bytes +@item -g port +Wait gdb connection to port @end table Environment variables: @@ -2850,6 +2910,8 @@ The binary format is detected automatically. +@command{qemu-sparc} can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). + @command{qemu-sparc32plus} can execute Sparc32 and SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). diff -Nur kvm-76/qemu/qemu-kvm-ia64.c kvm-userspace/qemu/qemu-kvm-ia64.c --- kvm-76/qemu/qemu-kvm-ia64.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-kvm-ia64.c 2008-10-12 09:38:24.000000000 +0200 @@ -57,6 +57,11 @@ return 1; } +int kvm_arch_try_push_nmi(void *opaque) +{ + return 1; +} + void kvm_arch_update_regs_for_sipi(CPUState *env) { } diff -Nur kvm-76/qemu/qemu-kvm-x86.c kvm-userspace/qemu/qemu-kvm-x86.c --- kvm-76/qemu/qemu-kvm-x86.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-kvm-x86.c 2008-10-12 09:38:24.000000000 +0200 @@ -27,6 +27,18 @@ static int lm_capable_kernel; +int kvm_qemu_create_memory_alias(uint64_t phys_start, + uint64_t len, + uint64_t target_phys) +{ + return kvm_create_memory_alias(kvm_context, phys_start, len, target_phys); +} + +int kvm_qemu_destroy_memory_alias(uint64_t phys_start) +{ + return kvm_destroy_memory_alias(kvm_context, phys_start); +} + int kvm_arch_qemu_create_context(void) { int i; @@ -598,7 +610,8 @@ CPUState *env = cpu_single_env; if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK))) { + (env->eflags & IF_MASK)) && + !(env->interrupt_request & CPU_INTERRUPT_NMI)) { env->halted = 1; env->exception_index = EXCP_HLT; } @@ -627,8 +640,9 @@ int kvm_arch_has_work(CPUState *env) { - if ((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) && - (env->eflags & IF_MASK)) + if (((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) && + (env->eflags & IF_MASK)) || + (env->interrupt_request & CPU_INTERRUPT_NMI)) return 1; return 0; } @@ -653,6 +667,24 @@ return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0; } +int kvm_arch_try_push_nmi(void *opaque) +{ + CPUState *env = cpu_single_env; + int r; + + if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) + return 0; + + if (kvm_is_ready_for_nmi_injection(kvm_context, env->cpu_index)) { + env->interrupt_request &= ~CPU_INTERRUPT_NMI; + r = kvm_inject_nmi(kvm_context, env->cpu_index); + if (r < 0) + printf("cpu %d fail inject NMI\n", env->cpu_index); + } + + return (env->interrupt_request & CPU_INTERRUPT_NMI) != 0; +} + void kvm_arch_update_regs_for_sipi(CPUState *env) { SegmentCache cs = env->segs[R_CS]; diff -Nur kvm-76/qemu/qemu-kvm.c kvm-userspace/qemu/qemu-kvm.c --- kvm-76/qemu/qemu-kvm.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-kvm.c 2008-10-12 09:38:24.000000000 +0200 @@ -125,6 +125,16 @@ qemu_cond_wait(&qemu_work_cond); } +static void inject_interrupt(void *data) +{ + cpu_interrupt(vcpu->env, (int)data); +} + +void kvm_inject_interrupt(CPUState *env, int mask) +{ + on_vcpu(env, inject_interrupt, (void *)mask); +} + void kvm_update_interrupt_request(CPUState *env) { int signal = 0; @@ -163,6 +173,11 @@ return kvm_arch_try_push_interrupts(opaque); } +static int try_push_nmi(void *opaque) +{ + return kvm_arch_try_push_nmi(opaque); +} + static void post_kvm_run(void *opaque, int vcpu) { @@ -394,7 +409,7 @@ while (1) { while (!has_work(env)) kvm_main_loop_wait(env, 1000); - if (env->interrupt_request & CPU_INTERRUPT_HARD) + if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) env->halted = 0; if (!kvm_irqchip_in_kernel(kvm_context) && info->sipi_needed) update_regs_for_sipi(env); @@ -545,7 +560,7 @@ io_thread = pthread_self(); qemu_system_ready = 1; - if (kvm_eventfd(fds) == -1) { + if (qemu_eventfd(fds) == -1) { fprintf(stderr, "failed to create eventfd\n"); return -errno; } @@ -716,6 +731,7 @@ .shutdown = kvm_shutdown, .io_window = kvm_io_window, .try_push_interrupts = try_push_interrupts, + .try_push_nmi = try_push_nmi, .post_kvm_run = post_kvm_run, .pre_kvm_run = pre_kvm_run, #ifdef TARGET_I386 @@ -794,6 +810,13 @@ return; } +void kvm_cpu_unregister_physical_memory(target_phys_addr_t start_addr, + target_phys_addr_t size, + unsigned long phys_offset) +{ + kvm_unregister_memory_area(kvm_context, start_addr, size); +} + int kvm_setup_guest_memory(void *area, unsigned long size) { int ret = 0; @@ -938,6 +961,15 @@ return r; } +void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, + int log) +{ + if (log) + kvm_dirty_pages_log_enable_slot(kvm_context, start, size); + else + kvm_dirty_pages_log_disable_slot(kvm_context, start, size); +} + int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap) { unsigned int bsize = BITMAP_SIZE(phys_ram_size); diff -Nur kvm-76/qemu/qemu-kvm.h kvm-userspace/qemu/qemu-kvm.h --- kvm-76/qemu/qemu-kvm.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/qemu-kvm.h 2008-10-12 09:38:24.000000000 +0200 @@ -35,17 +35,27 @@ void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env); void qemu_kvm_cpuid_on_env(CPUState *env); +void kvm_inject_interrupt(CPUState *env, int mask); void kvm_update_after_sipi(CPUState *env); void kvm_update_interrupt_request(CPUState *env); void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr, unsigned long size, unsigned long phys_offset); +void kvm_cpu_unregister_physical_memory(target_phys_addr_t start_addr, + target_phys_addr_t size, + unsigned long phys_offset); void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, unsigned long size, int log, int writable); void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr, unsigned long size); +void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, + int log); int kvm_setup_guest_memory(void *area, unsigned long size); +int kvm_qemu_create_memory_alias(uint64_t phys_start, + uint64_t len, + uint64_t target_phys); +int kvm_qemu_destroy_memory_alias(uint64_t phys_start); int kvm_arch_qemu_create_context(void); @@ -57,6 +67,7 @@ void kvm_arch_post_kvm_run(void *opaque, int vcpu); int kvm_arch_has_work(CPUState *env); int kvm_arch_try_push_interrupts(void *opaque); +int kvm_arch_try_push_nmi(void *opaque); void kvm_arch_update_regs_for_sipi(CPUState *env); void kvm_arch_cpu_reset(CPUState *env); @@ -87,8 +98,6 @@ int handle_powerpc_dcr_write(int vcpu,uint32_t dcrn, uint32_t data); #endif -int kvm_eventfd(int *fds); - #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) #define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8) @@ -107,6 +116,8 @@ #define qemu_kvm_irqchip_in_kernel() (0) #define qemu_kvm_pit_in_kernel() (0) #define qemu_kvm_has_sync_mmu() (0) +#define kvm_load_registers(env) do {} while(0) +#define kvm_save_registers(env) do {} while(0) #endif void kvm_mutex_unlock(void); diff -Nur kvm-76/qemu/sdl_keysym.h kvm-userspace/qemu/sdl_keysym.h --- kvm-76/qemu/sdl_keysym.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/sdl_keysym.h 2008-10-12 09:38:24.000000000 +0200 @@ -2,7 +2,7 @@ const char* name; int keysym; } name2keysym_t; -static name2keysym_t name2keysym[]={ +static const name2keysym_t name2keysym[]={ /* ascii */ { "space", 0x020}, { "exclam", 0x021}, diff -Nur kvm-76/qemu/slirp/bootp.c kvm-userspace/qemu/slirp/bootp.c --- kvm-76/qemu/slirp/bootp.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/slirp/bootp.c 2008-10-12 09:38:24.000000000 +0200 @@ -36,7 +36,7 @@ uint8_t macaddr[6]; } BOOTPClient; -BOOTPClient bootp_clients[NB_ADDR]; +static BOOTPClient bootp_clients[NB_ADDR]; const char *bootp_filename; diff -Nur kvm-76/qemu/slirp/if.c kvm-userspace/qemu/slirp/if.c --- kvm-76/qemu/slirp/if.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/slirp/if.c 2008-10-12 09:38:24.000000000 +0200 @@ -15,7 +15,7 @@ #define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm)) -void +static void ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead) { ifm->ifs_next = ifmhead->ifs_next; @@ -24,7 +24,7 @@ ifm->ifs_next->ifs_prev = ifm; } -void +static void ifs_remque(struct mbuf *ifm) { ifm->ifs_prev->ifs_next = ifm->ifs_next; diff -Nur kvm-76/qemu/slirp/ip_icmp.c kvm-userspace/qemu/slirp/ip_icmp.c --- kvm-76/qemu/slirp/ip_icmp.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/slirp/ip_icmp.c 2008-10-12 09:38:24.000000000 +0200 @@ -43,7 +43,7 @@ /* The message sent when emulating PING */ /* Be nice and tell them it's just a pseudo-ping packet */ -const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; +static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; /* list of actions for icmp_error() on RX of an icmp message */ static const int icmp_flush[19] = { diff -Nur kvm-76/qemu/slirp/mbuf.c kvm-userspace/qemu/slirp/mbuf.c --- kvm-76/qemu/slirp/mbuf.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/slirp/mbuf.c 2008-10-12 09:38:24.000000000 +0200 @@ -17,8 +17,6 @@ #include -struct mbuf *mbutl; -char *mclrefcnt; int mbuf_alloced = 0; struct mbuf m_freelist, m_usedlist; #define MBUF_THRESH 30 diff -Nur kvm-76/qemu/slirp/socket.c kvm-userspace/qemu/slirp/socket.c --- kvm-76/qemu/slirp/socket.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/slirp/socket.c 2008-10-12 09:38:24.000000000 +0200 @@ -116,6 +116,8 @@ len = sb->sb_datalen - sb->sb_cc; iov[0].iov_base = sb->sb_wptr; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; if (sb->sb_wptr < sb->sb_rptr) { iov[0].iov_len = sb->sb_rptr - sb->sb_wptr; /* Should never succeed, but... */ @@ -319,6 +321,8 @@ len = sb->sb_cc; iov[0].iov_base = sb->sb_rptr; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; if (sb->sb_rptr < sb->sb_wptr) { iov[0].iov_len = sb->sb_wptr - sb->sb_rptr; /* Should never succeed, but... */ diff -Nur kvm-76/qemu/sparc-dis.c kvm-userspace/qemu/sparc-dis.c --- kvm-76/qemu/sparc-dis.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/sparc-dis.c 2008-10-12 09:38:24.000000000 +0200 @@ -67,11 +67,7 @@ short supported; }; -extern const struct sparc_opcode_arch sparc_opcode_archs[]; - -/* Given architecture name, look up it's sparc_opcode_arch_val value. */ -extern enum sparc_opcode_arch_val sparc_opcode_lookup_arch - PARAMS ((const char *)); +static const struct sparc_opcode_arch sparc_opcode_archs[]; /* Return the bitmask of supported architectures for ARCH. */ #define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported) @@ -216,17 +212,14 @@ #define RS1_G0 RS1(~0) #define RS2_G0 RS2(~0) -extern const struct sparc_opcode sparc_opcodes[]; -extern const int sparc_num_opcodes; +static const struct sparc_opcode sparc_opcodes[]; +static const int sparc_num_opcodes; -extern int sparc_encode_asi PARAMS ((const char *)); -extern const char *sparc_decode_asi PARAMS ((int)); -extern int sparc_encode_membar PARAMS ((const char *)); -extern const char *sparc_decode_membar PARAMS ((int)); -extern int sparc_encode_prefetch PARAMS ((const char *)); -extern const char *sparc_decode_prefetch PARAMS ((int)); -extern int sparc_encode_sparclet_cpreg PARAMS ((const char *)); -extern const char *sparc_decode_sparclet_cpreg PARAMS ((int)); +static const char *sparc_decode_asi_v8 PARAMS ((int)); +static const char *sparc_decode_asi_v9 PARAMS ((int)); +static const char *sparc_decode_membar PARAMS ((int)); +static const char *sparc_decode_prefetch PARAMS ((int)); +static const char *sparc_decode_sparclet_cpreg PARAMS ((int)); /* Some defines to make life easy. */ #define MASK_V6 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V6) @@ -270,7 +263,7 @@ /* Table of opcode architectures. The order is defined in opcode/sparc.h. */ -const struct sparc_opcode_arch sparc_opcode_archs[] = { +static const struct sparc_opcode_arch sparc_opcode_archs[] = { { "v6", MASK_V6 }, { "v7", MASK_V6 | MASK_V7 }, { "v8", MASK_V6 | MASK_V7 | MASK_V8 }, @@ -285,23 +278,6 @@ { NULL, 0 } }; -/* Given NAME, return it's architecture entry. */ - -enum sparc_opcode_arch_val -sparc_opcode_lookup_arch (name) - const char *name; -{ - const struct sparc_opcode_arch *p; - - for (p = &sparc_opcode_archs[0]; p->name; ++p) - { - if (strcmp (name, p->name) == 0) - return (enum sparc_opcode_arch_val) (p - &sparc_opcode_archs[0]); - } - - return SPARC_OPCODE_ARCH_BAD; -} - /* Branch condition field. */ #define COND(x) (((x)&0xf)<<25) @@ -374,7 +350,7 @@ { opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "1,i,d", 0, arch_mask }, \ { opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "i,1,d", 0, arch_mask } -const struct sparc_opcode sparc_opcodes[] = { +static const struct sparc_opcode sparc_opcodes[] = { { "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 }, { "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */ @@ -2055,7 +2031,7 @@ }; -const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0])); +static const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0])); /* Utilities for argument parsing. */ @@ -2067,23 +2043,8 @@ /* Look up NAME in TABLE. */ -static int lookup_name PARAMS ((const arg *, const char *)); static const char *lookup_value PARAMS ((const arg *, int)); -static int -lookup_name (table, name) - const arg *table; - const char *name; -{ - const arg *p; - - for (p = table; p->name; ++p) - if (strcmp (name, p->name) == 0) - return p->value; - - return -1; -} - /* Look up VALUE in TABLE. */ static const char * @@ -2218,15 +2179,6 @@ { 0, 0 } }; -/* Return the value for membar arg NAME, or -1 if not found. */ - -int -sparc_encode_membar (name) - const char *name; -{ - return lookup_name (membar_table, name); -} - /* Return the name for membar value VALUE or NULL if not found. */ const char * @@ -2249,15 +2201,6 @@ { 0, 0 } }; -/* Return the value for prefetch arg NAME, or -1 if not found. */ - -int -sparc_encode_prefetch (name) - const char *name; -{ - return lookup_name (prefetch_table, name); -} - /* Return the name for prefetch value VALUE or NULL if not found. */ const char * @@ -2281,15 +2224,6 @@ { 0, 0 } }; -/* Return the value for sparclet cpreg arg NAME, or -1 if not found. */ - -int -sparc_encode_sparclet_cpreg (name) - const char *name; -{ - return lookup_name (sparclet_cpreg_table, name); -} - /* Return the name for sparclet cpreg value VALUE or NULL if not found. */ const char * diff -Nur kvm-76/qemu/sysemu.h kvm-userspace/qemu/sysemu.h --- kvm-76/qemu/sysemu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/sysemu.h 2008-10-12 09:38:24.000000000 +0200 @@ -88,6 +88,7 @@ extern int graphic_width; extern int graphic_height; extern int graphic_depth; +extern int nographic; extern const char *keyboard_layout; extern int win2k_install_hack; extern int alt_grab; @@ -97,7 +98,6 @@ extern int graphic_rotate; extern int no_quit; extern int semihosting_enabled; -extern int autostart; extern int old_param; extern int hpagesize; extern const char *bootp_filename; diff -Nur kvm-76/qemu/target-alpha/STATUS kvm-userspace/qemu/target-alpha/STATUS --- kvm-76/qemu/target-alpha/STATUS 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/STATUS 2008-10-12 09:38:24.000000000 +0200 @@ -5,18 +5,13 @@ exec.h : CPU definitions used only for translated code execution helper.c : helpers that can be called either by the translated code or the Qemu core, including the exception handler. -op.c : Alpha dedicated micro-operations used by the code translator -op_helper.c : helpers that can be called only from micro-operations -op_helper.h : micro-operations helpers prototypes -op_helper_mem.h : micro-operations templates - for load and stores accesses helpers -op_mem.h : micro-operations templates for load and stores accesses -op_template.h : micro-operation templates for register moves +op_helper.c : helpers that can be called only from TCG +helper.h : TCG helpers prototypes translate.c : Alpha instructions to micro-operations translator Code translator status: The Alpha CPU instruction emulation should be quite complete with the -limitation that the VAX floating-point load and stores are not implemented. +limitation that the VAX floating-point load and stores are not tested. The 4 MMU modes are implemented. Linux user mode emulation status: diff -Nur kvm-76/qemu/target-alpha/cpu.h kvm-userspace/qemu/target-alpha/cpu.h --- kvm-76/qemu/target-alpha/cpu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/cpu.h 2008-10-12 09:38:24.000000000 +0200 @@ -277,8 +277,6 @@ */ target_ulong t0, t1; #endif - /* */ - double ft0, ft1, ft2; /* Those resources are used only in Qemu core */ CPU_COMMON diff -Nur kvm-76/qemu/target-alpha/exec.h kvm-userspace/qemu/target-alpha/exec.h --- kvm-76/qemu/target-alpha/exec.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/exec.h 2008-10-12 09:38:24.000000000 +0200 @@ -29,32 +29,10 @@ register struct CPUAlphaState *env asm(AREG0); -#if TARGET_LONG_BITS > HOST_LONG_BITS - -/* no registers can be used */ -#define T0 (env->t0) -#define T1 (env->t1) - -#else - -register uint64_t T0 asm(AREG1); -register uint64_t T1 asm(AREG2); - -#endif /* TARGET_LONG_BITS > HOST_LONG_BITS */ - #define PARAM(n) ((uint64_t)PARAM##n) #define SPARAM(n) ((int32_t)PARAM##n) -#define FT0 (env->ft0) -#define FT1 (env->ft1) -#define FT2 (env->ft2) #define FP_STATUS (env->fp_status) -#if defined (DEBUG_OP) -#define RETURN() __asm__ __volatile__("nop" : : : "memory"); -#else -#define RETURN() __asm__ __volatile__("" : : : "memory"); -#endif - #include "cpu.h" #include "exec-all.h" diff -Nur kvm-76/qemu/target-alpha/helper.c kvm-userspace/qemu/target-alpha/helper.c --- kvm-76/qemu/target-alpha/helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -434,21 +434,5 @@ if ((i % 3) == 2) cpu_fprintf(f, "\n"); } - cpu_fprintf(f, "FT " TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx, - *((uint64_t *)(&env->ft0)), *((uint64_t *)(&env->ft1)), - *((uint64_t *)(&env->ft2))); - cpu_fprintf(f, "\nMEM " TARGET_FMT_lx " %d %d\n", - ldq_raw(0x000000004007df60ULL), - (uint8_t *)(&env->ft0), (uint8_t *)(&env->fir[0])); } -void cpu_dump_EA (target_ulong EA) -{ - FILE *f; - - if (logfile) - f = logfile; - else - f = stdout; - fprintf(f, "Memory access at address " TARGET_FMT_lx "\n", EA); -} diff -Nur kvm-76/qemu/target-alpha/helper.h kvm-userspace/qemu/target-alpha/helper.h --- kvm-76/qemu/target-alpha/helper.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/helper.h 2008-10-12 09:38:24.000000000 +0200 @@ -41,3 +41,93 @@ DEF_HELPER(uint64_t, helper_insqh, (int64_t, uint64_t)) DEF_HELPER(uint64_t, helper_cmpbge, (uint64_t, uint64_t)) + +DEF_HELPER(uint64_t, helper_load_fpcr, (void)) +DEF_HELPER(void, helper_store_fpcr, (uint64_t val)) + +DEF_HELPER(uint32_t, helper_f_to_memory, (uint64_t s)) +DEF_HELPER(uint64_t, helper_memory_to_f, (uint32_t s)) +DEF_HELPER(uint64_t, helper_addf, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_subf, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_mulf, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_divf, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_sqrtf, (uint64_t)) + +DEF_HELPER(uint64_t, helper_g_to_memory, (uint64_t s)) +DEF_HELPER(uint64_t, helper_memory_to_g, (uint64_t s)) +DEF_HELPER(uint64_t, helper_addg, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_subg, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_mulg, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_divg, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_sqrtg, (uint64_t)) + +DEF_HELPER(uint32_t, helper_s_to_memory, (uint64_t s)) +DEF_HELPER(uint64_t, helper_memory_to_s, (uint32_t s)) +DEF_HELPER(uint64_t, helper_adds, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_subs, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_muls, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_divs, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_sqrts, (uint64_t)) + +DEF_HELPER(uint64_t, helper_addt, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_subt, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_mult, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_divt, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_sqrtt, (uint64_t)) + +DEF_HELPER(uint64_t, helper_cmptun, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmpteq, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmptle, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmptlt, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmpgeq, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmpgle, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cmpglt, (uint64_t, uint64_t)) + +DEF_HELPER(uint64_t, helper_cmpfeq, (uint64_t)) +DEF_HELPER(uint64_t, helper_cmpfne, (uint64_t)) +DEF_HELPER(uint64_t, helper_cmpflt, (uint64_t)) +DEF_HELPER(uint64_t, helper_cmpfle, (uint64_t)) +DEF_HELPER(uint64_t, helper_cmpfgt, (uint64_t)) +DEF_HELPER(uint64_t, helper_cmpfge, (uint64_t)) + +DEF_HELPER(uint64_t, helper_cpys, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cpysn, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_cpyse, (uint64_t, uint64_t)) + +DEF_HELPER(uint64_t, helper_cvtts, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtst, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvttq, (uint64_t)) +DEF_HELPER(uint32_t, helper_cvtqs, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtqt, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtqf, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtgf, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtgq, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtqg, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtlq, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtql, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtqlv, (uint64_t)) +DEF_HELPER(uint64_t, helper_cvtqlsv, (uint64_t)) + +#if !defined (CONFIG_USER_ONLY) +DEF_HELPER(void, helper_hw_rei, (void)) +DEF_HELPER(void, helper_hw_ret, (uint64_t)) +DEF_HELPER(uint64_t, helper_mfpr, (int, uint64_t)) +DEF_HELPER(void, helper_mtpr, (int, uint64_t)) +DEF_HELPER(void, helper_set_alt_mode, (void)) +DEF_HELPER(void, helper_restore_mode, (void)) + +DEF_HELPER(uint64_t, helper_ld_virt_to_phys, (uint64_t)) +DEF_HELPER(uint64_t, helper_st_virt_to_phys, (uint64_t)) +DEF_HELPER(void, helper_ldl_raw, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldq_raw, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldl_l_raw, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldq_l_raw, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldl_kernel, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldq_kernel, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldl_data, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_ldq_data, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_stl_raw, (uint64_t, uint64_t)) +DEF_HELPER(void, helper_stq_raw, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_stl_c_raw, (uint64_t, uint64_t)) +DEF_HELPER(uint64_t, helper_stq_c_raw, (uint64_t, uint64_t)) +#endif diff -Nur kvm-76/qemu/target-alpha/op_helper.c kvm-userspace/qemu/target-alpha/op_helper.c --- kvm-76/qemu/target-alpha/op_helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/op_helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -22,40 +22,11 @@ #include "host-utils.h" #include "softfloat.h" -#include "op_helper.h" - -#define MEMSUFFIX _raw -#include "op_helper_mem.h" - -#if !defined(CONFIG_USER_ONLY) -#define MEMSUFFIX _kernel -#include "op_helper_mem.h" - -#define MEMSUFFIX _executive -#include "op_helper_mem.h" - -#define MEMSUFFIX _supervisor -#include "op_helper_mem.h" - -#define MEMSUFFIX _user -#include "op_helper_mem.h" - -/* This is used for pal modes */ -#define MEMSUFFIX _data -#include "op_helper_mem.h" -#endif - void helper_tb_flush (void) { tlb_flush(env, 1); } -void cpu_dump_EA (target_ulong EA); -void helper_print_mem_EA (target_ulong EA) -{ - cpu_dump_EA(EA); -} - /*****************************************************************************/ /* Exceptions processing helpers */ void helper_excp (int excp, int error) @@ -91,37 +62,38 @@ return env->implver; } -void helper_load_fpcr (void) +uint64_t helper_load_fpcr (void) { - T0 = 0; + uint64_t ret = 0; #ifdef CONFIG_SOFTFLOAT - T0 |= env->fp_status.float_exception_flags << 52; + ret |= env->fp_status.float_exception_flags << 52; if (env->fp_status.float_exception_flags) - T0 |= 1ULL << 63; + ret |= 1ULL << 63; env->ipr[IPR_EXC_SUM] &= ~0x3E: env->ipr[IPR_EXC_SUM] |= env->fp_status.float_exception_flags << 1; #endif switch (env->fp_status.float_rounding_mode) { case float_round_nearest_even: - T0 |= 2ULL << 58; + ret |= 2ULL << 58; break; case float_round_down: - T0 |= 1ULL << 58; + ret |= 1ULL << 58; break; case float_round_up: - T0 |= 3ULL << 58; + ret |= 3ULL << 58; break; case float_round_to_zero: break; } + return ret; } -void helper_store_fpcr (void) +void helper_store_fpcr (uint64_t val) { #ifdef CONFIG_SOFTFLOAT - set_float_exception_flags((T0 >> 52) & 0x3F, &FP_STATUS); + set_float_exception_flags((val >> 52) & 0x3F, &FP_STATUS); #endif - switch ((T0 >> 58) & 3) { + switch ((val >> 58) & 3) { case 0: set_float_rounding_mode(float_round_to_zero, &FP_STATUS); break; @@ -367,707 +339,691 @@ return res; } -void helper_cmov_fir (int freg) +/* Floating point helpers */ + +/* F floating (VAX) */ +static always_inline uint64_t float32_to_f (float32 fa) { - if (FT0 != 0) - env->fir[freg] = FT1; + uint32_t a; + uint64_t r, exp, mant, sig; + + a = *(uint32_t*)(&fa); + sig = ((uint64_t)a & 0x80000000) << 32; + exp = (a >> 23) & 0xff; + mant = ((uint64_t)a & 0x007fffff) << 29; + + if (exp == 255) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 253) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; } -void helper_sqrts (void) +static always_inline float32 f_to_float32 (uint64_t a) { - FT0 = float32_sqrt(FT0, &FP_STATUS); + uint32_t r, exp, mant_sig; + + exp = ((a >> 55) & 0x80) | ((a >> 52) & 0x7f); + mant_sig = ((a >> 32) & 0x80000000) | ((a >> 29) & 0x007fffff); + + if (unlikely(!exp && mant_sig)) { + /* Reserved operands / Dirty zero */ + helper_excp(EXCP_OPCDEC, 0); + } + + if (exp < 3) { + /* Underflow */ + r = 0; + } else { + r = ((exp - 2) << 23) | mant_sig; + } + + return *(float32*)(&a); } -void helper_cpys (void) +uint32_t helper_f_to_memory (uint64_t a) { - union { - double d; - uint64_t i; - } p, q, r; + uint32_t r; + r = (a & 0x00001fffe0000000ull) >> 13; + r |= (a & 0x07ffe00000000000ull) >> 45; + r |= (a & 0xc000000000000000ull) >> 48; + return r; +} - p.d = FT0; - q.d = FT1; - r.i = p.i & 0x8000000000000000ULL; - r.i |= q.i & ~0x8000000000000000ULL; - FT0 = r.d; +uint64_t helper_memory_to_f (uint32_t a) +{ + uint64_t r; + r = ((uint64_t)(a & 0x0000c000)) << 48; + r |= ((uint64_t)(a & 0x003fffff)) << 45; + r |= ((uint64_t)(a & 0xffff0000)) << 13; + if (!(a & 0x00004000)) + r |= 0x7ll << 59; + return r; } -void helper_cpysn (void) +uint64_t helper_addf (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p, q, r; + float32 fa, fb, fr; - p.d = FT0; - q.d = FT1; - r.i = (~p.i) & 0x8000000000000000ULL; - r.i |= q.i & ~0x8000000000000000ULL; - FT0 = r.d; + fa = f_to_float32(a); + fb = f_to_float32(b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_f(fr); } -void helper_cpyse (void) +uint64_t helper_subf (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p, q, r; + float32 fa, fb, fr; - p.d = FT0; - q.d = FT1; - r.i = p.i & 0xFFF0000000000000ULL; - r.i |= q.i & ~0xFFF0000000000000ULL; - FT0 = r.d; + fa = f_to_float32(a); + fb = f_to_float32(b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_f(fr); } -void helper_itofs (void) +uint64_t helper_mulf (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p; + float32 fa, fb, fr; - p.d = FT0; - FT0 = int64_to_float32(p.i, &FP_STATUS); + fa = f_to_float32(a); + fb = f_to_float32(b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_f(fr); } -void helper_ftois (void) +uint64_t helper_divf (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p; + float32 fa, fb, fr; - p.i = float32_to_int64(FT0, &FP_STATUS); - FT0 = p.d; + fa = f_to_float32(a); + fb = f_to_float32(b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_f(fr); } -void helper_sqrtt (void) +uint64_t helper_sqrtf (uint64_t t) { - FT0 = float64_sqrt(FT0, &FP_STATUS); + float32 ft, fr; + + ft = f_to_float32(t); + fr = float32_sqrt(ft, &FP_STATUS); + return float32_to_f(fr); } -void helper_cmptun (void) + +/* G floating (VAX) */ +static always_inline uint64_t float64_to_g (float64 fa) { - union { - double d; - uint64_t i; - } p; + uint64_t a, r, exp, mant, sig; - p.i = 0; - if (float64_is_nan(FT0) || float64_is_nan(FT1)) - p.i = 0x4000000000000000ULL; - FT0 = p.d; + a = *(uint64_t*)(&fa); + sig = a & 0x8000000000000000ull; + exp = (a >> 52) & 0x7ff; + mant = a & 0x000fffffffffffffull; + + if (exp == 2047) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 2045) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; } -void helper_cmpteq (void) +static always_inline float64 g_to_float64 (uint64_t a) { - union { - double d; - uint64_t i; - } p; + uint64_t r, exp, mant_sig; + + exp = (a >> 52) & 0x7ff; + mant_sig = a & 0x800fffffffffffffull; + + if (!exp && mant_sig) { + /* Reserved operands / Dirty zero */ + helper_excp(EXCP_OPCDEC, 0); + } - p.i = 0; - if (float64_eq(FT0, FT1, &FP_STATUS)) - p.i = 0x4000000000000000ULL; - FT0 = p.d; + if (exp < 3) { + /* Underflow */ + r = 0; + } else { + r = ((exp - 2) << 52) | mant_sig; + } + + return *(float64*)(&a); } -void helper_cmptle (void) +uint64_t helper_g_to_memory (uint64_t a) { - union { - double d; - uint64_t i; - } p; + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; +} - p.i = 0; - if (float64_le(FT0, FT1, &FP_STATUS)) - p.i = 0x4000000000000000ULL; - FT0 = p.d; +uint64_t helper_memory_to_g (uint64_t a) +{ + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; } -void helper_cmptlt (void) +uint64_t helper_addg (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p; + float64 fa, fb, fr; - p.i = 0; - if (float64_lt(FT0, FT1, &FP_STATUS)) - p.i = 0x4000000000000000ULL; - FT0 = p.d; + fa = g_to_float64(a); + fb = g_to_float64(b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_g(fr); } -void helper_itoft (void) +uint64_t helper_subg (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p; + float64 fa, fb, fr; - p.d = FT0; - FT0 = int64_to_float64(p.i, &FP_STATUS); + fa = g_to_float64(a); + fb = g_to_float64(b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_g(fr); } -void helper_ftoit (void) +uint64_t helper_mulg (uint64_t a, uint64_t b) { - union { - double d; - uint64_t i; - } p; + float64 fa, fb, fr; - p.i = float64_to_int64(FT0, &FP_STATUS); - FT0 = p.d; + fa = g_to_float64(a); + fb = g_to_float64(b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_g(fr); } -static always_inline int vaxf_is_valid (float ff) +uint64_t helper_divg (uint64_t a, uint64_t b) { - union { - float f; - uint32_t i; - } p; - uint32_t exp, mant; + float64 fa, fb, fr; - p.f = ff; - exp = (p.i >> 23) & 0xFF; - mant = p.i & 0x007FFFFF; - if (exp == 0 && ((p.i & 0x80000000) || mant != 0)) { - /* Reserved operands / Dirty zero */ - return 0; - } + fa = g_to_float64(a); + fb = g_to_float64(b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_sqrtg (uint64_t a) +{ + float64 fa, fr; - return 1; + fa = g_to_float64(a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_g(fr); } -static always_inline float vaxf_to_ieee32 (float ff) + +/* S floating (single) */ +static always_inline uint64_t float32_to_s (float32 fa) { - union { - float f; - uint32_t i; - } p; - uint32_t exp; + uint32_t a; + uint64_t r; - p.f = ff; - exp = (p.i >> 23) & 0xFF; - if (exp < 3) { - /* Underflow */ - p.f = 0.0; - } else { - p.f *= 0.25; - } + a = *(uint32_t*)(&fa); - return p.f; + r = (((uint64_t)(a & 0xc0000000)) << 32) | (((uint64_t)(a & 0x3fffffff)) << 29); + if (((a & 0x7f800000) != 0x7f800000) && (!(a & 0x40000000))) + r |= 0x7ll << 59; + return r; } -static always_inline float ieee32_to_vaxf (float fi) +static always_inline float32 s_to_float32 (uint64_t a) { - union { - float f; - uint32_t i; - } p; - uint32_t exp, mant; + uint32_t r = ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); + return *(float32*)(&r); +} - p.f = fi; - exp = (p.i >> 23) & 0xFF; - mant = p.i & 0x007FFFFF; - if (exp == 255) { - /* NaN or infinity */ - p.i = 1; - } else if (exp == 0) { - if (mant == 0) { - /* Zero */ - p.i = 0; - } else { - /* Denormalized */ - p.f *= 2.0; - } - } else { - if (exp >= 253) { - /* Overflow */ - p.i = 1; - } else { - p.f *= 4.0; - } - } +uint32_t helper_s_to_memory (uint64_t a) +{ + /* Memory format is the same as float32 */ + float32 fa = s_to_float32(a); + return *(uint32_t*)(&fa); +} - return p.f; +uint64_t helper_memory_to_s (uint32_t a) +{ + /* Memory format is the same as float32 */ + return float32_to_s(*(float32*)(&a)); } -void helper_addf (void) +uint64_t helper_adds (uint64_t a, uint64_t b) { - float ft0, ft1, ft2; + float32 fa, fb, fr; - if (!vaxf_is_valid(FT0) || !vaxf_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxf_to_ieee32(FT0); - ft1 = vaxf_to_ieee32(FT1); - ft2 = float32_add(ft0, ft1, &FP_STATUS); - FT0 = ieee32_to_vaxf(ft2); + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_s(fr); } -void helper_subf (void) +uint64_t helper_subs (uint64_t a, uint64_t b) { - float ft0, ft1, ft2; + float32 fa, fb, fr; - if (!vaxf_is_valid(FT0) || !vaxf_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxf_to_ieee32(FT0); - ft1 = vaxf_to_ieee32(FT1); - ft2 = float32_sub(ft0, ft1, &FP_STATUS); - FT0 = ieee32_to_vaxf(ft2); + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_s(fr); } -void helper_mulf (void) +uint64_t helper_muls (uint64_t a, uint64_t b) { - float ft0, ft1, ft2; + float32 fa, fb, fr; - if (!vaxf_is_valid(FT0) || !vaxf_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxf_to_ieee32(FT0); - ft1 = vaxf_to_ieee32(FT1); - ft2 = float32_mul(ft0, ft1, &FP_STATUS); - FT0 = ieee32_to_vaxf(ft2); + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_s(fr); } -void helper_divf (void) +uint64_t helper_divs (uint64_t a, uint64_t b) { - float ft0, ft1, ft2; + float32 fa, fb, fr; - if (!vaxf_is_valid(FT0) || !vaxf_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxf_to_ieee32(FT0); - ft1 = vaxf_to_ieee32(FT1); - ft2 = float32_div(ft0, ft1, &FP_STATUS); - FT0 = ieee32_to_vaxf(ft2); + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_s(fr); } -void helper_sqrtf (void) +uint64_t helper_sqrts (uint64_t a) { - float ft0, ft1; + float32 fa, fr; - if (!vaxf_is_valid(FT0) || !vaxf_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxf_to_ieee32(FT0); - ft1 = float32_sqrt(ft0, &FP_STATUS); - FT0 = ieee32_to_vaxf(ft1); + fa = s_to_float32(a); + fr = float32_sqrt(fa, &FP_STATUS); + return float32_to_s(fr); } -void helper_itoff (void) + +/* T floating (double) */ +static always_inline float64 t_to_float64 (uint64_t a) { - /* XXX: TODO */ + /* Memory format is the same as float64 */ + return *(float64*)(&a); } -static always_inline int vaxg_is_valid (double ff) +static always_inline uint64_t float64_to_t (float64 fa) { - union { - double f; - uint64_t i; - } p; - uint64_t exp, mant; - - p.f = ff; - exp = (p.i >> 52) & 0x7FF; - mant = p.i & 0x000FFFFFFFFFFFFFULL; - if (exp == 0 && ((p.i & 0x8000000000000000ULL) || mant != 0)) { - /* Reserved operands / Dirty zero */ - return 0; - } - - return 1; + /* Memory format is the same as float64 */ + return *(uint64*)(&fa); } -static always_inline double vaxg_to_ieee64 (double fg) +uint64_t helper_addt (uint64_t a, uint64_t b) { - union { - double f; - uint64_t i; - } p; - uint32_t exp; - - p.f = fg; - exp = (p.i >> 52) & 0x7FF; - if (exp < 3) { - /* Underflow */ - p.f = 0.0; - } else { - p.f *= 0.25; - } + float64 fa, fb, fr; - return p.f; + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_t(fr); } -static always_inline double ieee64_to_vaxg (double fi) +uint64_t helper_subt (uint64_t a, uint64_t b) { - union { - double f; - uint64_t i; - } p; - uint64_t mant; - uint32_t exp; - - p.f = fi; - exp = (p.i >> 52) & 0x7FF; - mant = p.i & 0x000FFFFFFFFFFFFFULL; - if (exp == 255) { - /* NaN or infinity */ - p.i = 1; /* VAX dirty zero */ - } else if (exp == 0) { - if (mant == 0) { - /* Zero */ - p.i = 0; - } else { - /* Denormalized */ - p.f *= 2.0; - } - } else { - if (exp >= 2045) { - /* Overflow */ - p.i = 1; /* VAX dirty zero */ - } else { - p.f *= 4.0; - } - } + float64 fa, fb, fr; - return p.f; + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_t(fr); } -void helper_addg (void) +uint64_t helper_mult (uint64_t a, uint64_t b) { - double ft0, ft1, ft2; + float64 fa, fb, fr; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - ft2 = float64_add(ft0, ft1, &FP_STATUS); - FT0 = ieee64_to_vaxg(ft2); + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_t(fr); } -void helper_subg (void) +uint64_t helper_divt (uint64_t a, uint64_t b) { - double ft0, ft1, ft2; + float64 fa, fb, fr; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - ft2 = float64_sub(ft0, ft1, &FP_STATUS); - FT0 = ieee64_to_vaxg(ft2); + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_t(fr); } -void helper_mulg (void) +uint64_t helper_sqrtt (uint64_t a) { - double ft0, ft1, ft2; + float64 fa, fr; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - ft2 = float64_mul(ft0, ft1, &FP_STATUS); - FT0 = ieee64_to_vaxg(ft2); + fa = t_to_float64(a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_t(fr); } -void helper_divg (void) + +/* Sign copy */ +uint64_t helper_cpys(uint64_t a, uint64_t b) { - double ft0, ft1, ft2; + return (a & 0x8000000000000000ULL) | (b & ~0x8000000000000000ULL); +} - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - ft2 = float64_div(ft0, ft1, &FP_STATUS); - FT0 = ieee64_to_vaxg(ft2); +uint64_t helper_cpysn(uint64_t a, uint64_t b) +{ + return ((~a) & 0x8000000000000000ULL) | (b & ~0x8000000000000000ULL); } -void helper_sqrtg (void) +uint64_t helper_cpyse(uint64_t a, uint64_t b) { - double ft0, ft1; + return (a & 0xFFF0000000000000ULL) | (b & ~0xFFF0000000000000ULL); +} - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = float64_sqrt(ft0, &FP_STATUS); - FT0 = ieee64_to_vaxg(ft1); + +/* Comparisons */ +uint64_t helper_cmptun (uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_is_nan(fa) || float64_is_nan(fb)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cmpgeq (void) +uint64_t helper_cmpteq(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; - double ft0, ft1; + float64 fa, fb; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - p.u = 0; - if (float64_eq(ft0, ft1, &FP_STATUS)) - p.u = 0x4000000000000000ULL; - FT0 = p.d; + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_eq(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cmpglt (void) +uint64_t helper_cmptle(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; - double ft0, ft1; + float64 fa, fb; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - p.u = 0; - if (float64_lt(ft0, ft1, &FP_STATUS)) - p.u = 0x4000000000000000ULL; - FT0 = p.d; + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_le(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cmpgle (void) +uint64_t helper_cmptlt(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; - double ft0, ft1; + float64 fa, fb; - if (!vaxg_is_valid(FT0) || !vaxg_is_valid(FT1)) { - /* XXX: TODO */ - } - ft0 = vaxg_to_ieee64(FT0); - ft1 = vaxg_to_ieee64(FT1); - p.u = 0; - if (float64_le(ft0, ft1, &FP_STATUS)) - p.u = 0x4000000000000000ULL; - FT0 = p.d; + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_lt(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cvtqs (void) +uint64_t helper_cmpgeq(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; + float64 fa, fb; - p.d = FT0; - FT0 = (float)p.u; + fa = g_to_float64(a); + fb = g_to_float64(b); + + if (float64_eq(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cvttq (void) +uint64_t helper_cmpgle(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; + float64 fa, fb; + + fa = g_to_float64(a); + fb = g_to_float64(b); - p.u = FT0; - FT0 = p.d; + if (float64_le(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cvtqt (void) +uint64_t helper_cmpglt(uint64_t a, uint64_t b) { - union { - double d; - uint64_t u; - } p; + float64 fa, fb; - p.d = FT0; - FT0 = p.u; + fa = g_to_float64(a); + fb = g_to_float64(b); + + if (float64_lt(fa, fb, &FP_STATUS)) + return 0x4000000000000000ULL; + else + return 0; } -void helper_cvtqf (void) +uint64_t helper_cmpfeq (uint64_t a) { - union { - double d; - uint64_t u; - } p; + return !(a & 0x7FFFFFFFFFFFFFFFULL); +} - p.d = FT0; - FT0 = ieee32_to_vaxf(p.u); +uint64_t helper_cmpfne (uint64_t a) +{ + return (a & 0x7FFFFFFFFFFFFFFFULL); } -void helper_cvtgf (void) +uint64_t helper_cmpflt (uint64_t a) { - double ft0; + return (a & 0x8000000000000000ULL) && (a & 0x7FFFFFFFFFFFFFFFULL); +} - ft0 = vaxg_to_ieee64(FT0); - FT0 = ieee32_to_vaxf(ft0); +uint64_t helper_cmpfle (uint64_t a) +{ + return (a & 0x8000000000000000ULL) || !(a & 0x7FFFFFFFFFFFFFFFULL); } -void helper_cvtgd (void) +uint64_t helper_cmpfgt (uint64_t a) { - /* XXX: TODO */ + return !(a & 0x8000000000000000ULL) && (a & 0x7FFFFFFFFFFFFFFFULL); } -void helper_cvtgq (void) +uint64_t helper_cmpfge (uint64_t a) { - union { - double d; - uint64_t u; - } p; + return !(a & 0x8000000000000000ULL) || !(a & 0x7FFFFFFFFFFFFFFFULL); +} + + +/* Floating point format conversion */ +uint64_t helper_cvtts (uint64_t a) +{ + float64 fa; + float32 fr; - p.u = vaxg_to_ieee64(FT0); - FT0 = p.d; + fa = t_to_float64(a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_s(fr); } -void helper_cvtqg (void) +uint64_t helper_cvtst (uint64_t a) { - union { - double d; - uint64_t u; - } p; + float32 fa; + float64 fr; - p.d = FT0; - FT0 = ieee64_to_vaxg(p.u); + fa = s_to_float32(a); + fr = float32_to_float64(fa, &FP_STATUS); + return float64_to_t(fr); } -void helper_cvtdg (void) +uint64_t helper_cvtqs (uint64_t a) { - /* XXX: TODO */ + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_s(fr); } -void helper_cvtlq (void) +uint64_t helper_cvttq (uint64_t a) { - union { - double d; - uint64_t u; - } p, q; - - p.d = FT0; - q.u = (p.u >> 29) & 0x3FFFFFFF; - q.u |= (p.u >> 32); - q.u = (int64_t)((int32_t)q.u); - FT0 = q.d; -} - -static always_inline void __helper_cvtql (int s, int v) -{ - union { - double d; - uint64_t u; - } p, q; - - p.d = FT0; - q.u = ((uint64_t)(p.u & 0xC0000000)) << 32; - q.u |= ((uint64_t)(p.u & 0x7FFFFFFF)) << 29; - FT0 = q.d; - if (v && (int64_t)((int32_t)p.u) != (int64_t)p.u) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); - } - if (s) { - /* TODO */ - } + float64 fa = t_to_float64(a); + return float64_to_int64_round_to_zero(fa, &FP_STATUS); } -void helper_cvtql (void) +uint64_t helper_cvtqt (uint64_t a) { - __helper_cvtql(0, 0); + float64 fr = int64_to_float64(a, &FP_STATUS); + return float64_to_t(fr); } -void helper_cvtqlv (void) +uint64_t helper_cvtqf (uint64_t a) { - __helper_cvtql(0, 1); + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_f(fr); } -void helper_cvtqlsv (void) +uint64_t helper_cvtgf (uint64_t a) { - __helper_cvtql(1, 1); + float64 fa; + float32 fr; + + fa = g_to_float64(a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_f(fr); } -void helper_cmpfeq (void) +uint64_t helper_cvtgq (uint64_t a) { - if (float64_eq(FT0, FT1, &FP_STATUS)) - T0 = 1; - else - T0 = 0; + float64 fa = g_to_float64(a); + return float64_to_int64_round_to_zero(fa, &FP_STATUS); } -void helper_cmpfne (void) +uint64_t helper_cvtqg (uint64_t a) { - if (float64_eq(FT0, FT1, &FP_STATUS)) - T0 = 0; - else - T0 = 1; + float64 fr; + fr = int64_to_float64(a, &FP_STATUS); + return float64_to_g(fr); } -void helper_cmpflt (void) +uint64_t helper_cvtlq (uint64_t a) { - if (float64_lt(FT0, FT1, &FP_STATUS)) - T0 = 1; - else - T0 = 0; + return (int64_t)((int32_t)((a >> 32) | ((a >> 29) & 0x3FFFFFFF))); } -void helper_cmpfle (void) +static always_inline uint64_t __helper_cvtql (uint64_t a, int s, int v) { - if (float64_lt(FT0, FT1, &FP_STATUS)) - T0 = 1; - else - T0 = 0; + uint64_t r; + + r = ((uint64_t)(a & 0xC0000000)) << 32; + r |= ((uint64_t)(a & 0x7FFFFFFF)) << 29; + + if (v && (int64_t)((int32_t)r) != (int64_t)r) { + helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + } + if (s) { + /* TODO */ + } + return r; } -void helper_cmpfgt (void) +uint64_t helper_cvtql (uint64_t a) { - if (float64_le(FT0, FT1, &FP_STATUS)) - T0 = 0; - else - T0 = 1; + return __helper_cvtql(a, 0, 0); } -void helper_cmpfge (void) +uint64_t helper_cvtqlv (uint64_t a) { - if (float64_lt(FT0, FT1, &FP_STATUS)) - T0 = 0; - else - T0 = 1; + return __helper_cvtql(a, 0, 1); +} + +uint64_t helper_cvtqlsv (uint64_t a) +{ + return __helper_cvtql(a, 1, 1); } +/* PALcode support special instructions */ #if !defined (CONFIG_USER_ONLY) -void helper_mfpr (int iprn) +void helper_hw_rei (void) { - uint64_t val; + env->pc = env->ipr[IPR_EXC_ADDR] & ~3; + env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1; + /* XXX: re-enable interrupts and memory mapping */ +} - if (cpu_alpha_mfpr(env, iprn, &val) == 0) - T0 = val; +void helper_hw_ret (uint64_t a) +{ + env->pc = a & ~3; + env->ipr[IPR_EXC_ADDR] = a & 1; + /* XXX: re-enable interrupts and memory mapping */ } -void helper_mtpr (int iprn) +uint64_t helper_mfpr (int iprn, uint64_t val) { - cpu_alpha_mtpr(env, iprn, T0, NULL); + uint64_t tmp; + + if (cpu_alpha_mfpr(env, iprn, &tmp) == 0) + val = tmp; + + return val; } -#endif -#if defined(HOST_SPARC) || defined(HOST_SPARC64) -void helper_reset_FT0 (void) +void helper_mtpr (int iprn, uint64_t val) { - FT0 = 0; + cpu_alpha_mtpr(env, iprn, val, NULL); } -void helper_reset_FT1 (void) +void helper_set_alt_mode (void) { - FT1 = 0; + env->saved_mode = env->ps & 0xC; + env->ps = (env->ps & ~0xC) | (env->ipr[IPR_ALT_MODE] & 0xC); } -void helper_reset_FT2 (void) +void helper_restore_mode (void) { - FT2 = 0; + env->ps = (env->ps & ~0xC) | env->saved_mode; } + #endif /*****************************************************************************/ @@ -1078,48 +1034,130 @@ * Hopefully, we emulate the PALcode, then we should never see * HW_LD / HW_ST instructions. */ -void helper_ld_phys_to_virt (void) +uint64_t helper_ld_virt_to_phys (uint64_t virtaddr) { uint64_t tlb_addr, physaddr; int index, mmu_idx; void *retaddr; mmu_idx = cpu_mmu_index(env); - index = (T0 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); redo: tlb_addr = env->tlb_table[mmu_idx][index].addr_read; - if ((T0 & TARGET_PAGE_MASK) == + if ((virtaddr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - physaddr = T0 + env->tlb_table[mmu_idx][index].addend; + physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend; } else { /* the page is not in the TLB : fill it */ retaddr = GETPC(); - tlb_fill(T0, 0, mmu_idx, retaddr); + tlb_fill(virtaddr, 0, mmu_idx, retaddr); goto redo; } - T0 = physaddr; + return physaddr; } -void helper_st_phys_to_virt (void) +uint64_t helper_st_virt_to_phys (uint64_t virtaddr) { uint64_t tlb_addr, physaddr; int index, mmu_idx; void *retaddr; mmu_idx = cpu_mmu_index(env); - index = (T0 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); redo: tlb_addr = env->tlb_table[mmu_idx][index].addr_write; - if ((T0 & TARGET_PAGE_MASK) == + if ((virtaddr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - physaddr = T0 + env->tlb_table[mmu_idx][index].addend; + physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend; } else { /* the page is not in the TLB : fill it */ retaddr = GETPC(); - tlb_fill(T0, 1, mmu_idx, retaddr); + tlb_fill(virtaddr, 1, mmu_idx, retaddr); goto redo; } - T0 = physaddr; + return physaddr; +} + +void helper_ldl_raw(uint64_t t0, uint64_t t1) +{ + ldl_raw(t1, t0); +} + +void helper_ldq_raw(uint64_t t0, uint64_t t1) +{ + ldq_raw(t1, t0); +} + +void helper_ldl_l_raw(uint64_t t0, uint64_t t1) +{ + env->lock = t1; + ldl_raw(t1, t0); +} + +void helper_ldq_l_raw(uint64_t t0, uint64_t t1) +{ + env->lock = t1; + ldl_raw(t1, t0); +} + +void helper_ldl_kernel(uint64_t t0, uint64_t t1) +{ + ldl_kernel(t1, t0); +} + +void helper_ldq_kernel(uint64_t t0, uint64_t t1) +{ + ldq_kernel(t1, t0); +} + +void helper_ldl_data(uint64_t t0, uint64_t t1) +{ + ldl_data(t1, t0); +} + +void helper_ldq_data(uint64_t t0, uint64_t t1) +{ + ldq_data(t1, t0); +} + +void helper_stl_raw(uint64_t t0, uint64_t t1) +{ + stl_raw(t1, t0); +} + +void helper_stq_raw(uint64_t t0, uint64_t t1) +{ + stq_raw(t1, t0); +} + +uint64_t helper_stl_c_raw(uint64_t t0, uint64_t t1) +{ + uint64_t ret; + + if (t1 == env->lock) { + stl_raw(t1, t0); + ret = 0; + } else + ret = 1; + + env->lock = 1; + + return ret; +} + +uint64_t helper_stq_c_raw(uint64_t t0, uint64_t t1) +{ + uint64_t ret; + + if (t1 == env->lock) { + stq_raw(t1, t0); + ret = 0; + } else + ret = 1; + + env->lock = 1; + + return ret; } #define MMUSUFFIX _mmu diff -Nur kvm-76/qemu/target-alpha/translate.c kvm-userspace/qemu/target-alpha/translate.c --- kvm-76/qemu/target-alpha/translate.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-alpha/translate.c 2008-10-12 09:38:24.000000000 +0200 @@ -31,7 +31,6 @@ #include "qemu-common.h" #define DO_SINGLE_STEP -#define GENERATE_NOP #define ALPHA_DEBUG_DISAS #define DO_TB_FLUSH @@ -48,13 +47,12 @@ /* global register indexes */ static TCGv cpu_env; static TCGv cpu_ir[31]; +static TCGv cpu_fir[31]; static TCGv cpu_pc; - -/* dyngen register indexes */ -static TCGv cpu_T[2]; +static TCGv cpu_lock; /* register names */ -static char cpu_reg_names[10*4+21*5]; +static char cpu_reg_names[10*4+21*5 + 10*5+21*6]; #include "gen-icount.h" @@ -69,27 +67,25 @@ cpu_env = tcg_global_reg_new(TCG_TYPE_PTR, TCG_AREG0, "env"); -#if TARGET_LONG_BITS > HOST_LONG_BITS - cpu_T[0] = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, - offsetof(CPUState, t0), "T0"); - cpu_T[1] = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, - offsetof(CPUState, t1), "T1"); -#else - cpu_T[0] = tcg_global_reg_new(TCG_TYPE_I64, TCG_AREG1, "T0"); - cpu_T[1] = tcg_global_reg_new(TCG_TYPE_I64, TCG_AREG2, "T1"); -#endif - p = cpu_reg_names; for (i = 0; i < 31; i++) { sprintf(p, "ir%d", i); cpu_ir[i] = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, offsetof(CPUState, ir[i]), p); p += (i < 10) ? 4 : 5; + + sprintf(p, "fir%d", i); + cpu_fir[i] = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, + offsetof(CPUState, fir[i]), p); + p += (i < 10) ? 5 : 6; } cpu_pc = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, offsetof(CPUState, pc), "pc"); + cpu_lock = tcg_global_mem_new(TCG_TYPE_I64, TCG_AREG0, + offsetof(CPUState, lock), "lock"); + /* register helpers */ #undef DEF_HELPER #define DEF_HELPER(ret, name, params) tcg_register_helper(name, #name); @@ -98,225 +94,169 @@ done_init = 1; } -static always_inline void gen_op_nop (void) +static always_inline void gen_excp (DisasContext *ctx, + int exception, int error_code) { -#if defined(GENERATE_NOP) - gen_op_no_op(); -#endif -} + TCGv tmp1, tmp2; -#define GEN32(func, NAME) \ -static GenOpFunc *NAME ## _table [32] = { \ -NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \ -NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \ -NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \ -NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \ -NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \ -NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \ -NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \ -NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \ -}; \ -static always_inline void func (int n) \ -{ \ - NAME ## _table[n](); \ -} - -/* FIR moves */ -/* Special hacks for fir31 */ -#define gen_op_load_FT0_fir31 gen_op_reset_FT0 -#define gen_op_load_FT1_fir31 gen_op_reset_FT1 -#define gen_op_load_FT2_fir31 gen_op_reset_FT2 -#define gen_op_store_FT0_fir31 gen_op_nop -#define gen_op_store_FT1_fir31 gen_op_nop -#define gen_op_store_FT2_fir31 gen_op_nop -#define gen_op_cmov_fir31 gen_op_nop -GEN32(gen_op_load_FT0_fir, gen_op_load_FT0_fir); -GEN32(gen_op_load_FT1_fir, gen_op_load_FT1_fir); -GEN32(gen_op_load_FT2_fir, gen_op_load_FT2_fir); -GEN32(gen_op_store_FT0_fir, gen_op_store_FT0_fir); -GEN32(gen_op_store_FT1_fir, gen_op_store_FT1_fir); -GEN32(gen_op_store_FT2_fir, gen_op_store_FT2_fir); -GEN32(gen_op_cmov_fir, gen_op_cmov_fir); - -static always_inline void gen_load_fir (DisasContext *ctx, int firn, int Tn) -{ - switch (Tn) { - case 0: - gen_op_load_FT0_fir(firn); - break; - case 1: - gen_op_load_FT1_fir(firn); - break; - case 2: - gen_op_load_FT2_fir(firn); - break; - } + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tmp1 = tcg_const_i32(exception); + tmp2 = tcg_const_i32(error_code); + tcg_gen_helper_0_2(helper_excp, tmp1, tmp2); + tcg_temp_free(tmp2); + tcg_temp_free(tmp1); } -static always_inline void gen_store_fir (DisasContext *ctx, int firn, int Tn) +static always_inline void gen_invalid (DisasContext *ctx) { - switch (Tn) { - case 0: - gen_op_store_FT0_fir(firn); - break; - case 1: - gen_op_store_FT1_fir(firn); - break; - case 2: - gen_op_store_FT2_fir(firn); - break; - } + gen_excp(ctx, EXCP_OPCDEC, 0); } -/* Memory moves */ -#if defined(CONFIG_USER_ONLY) -#define OP_LD_TABLE(width) \ -static GenOpFunc *gen_op_ld##width[] = { \ - &gen_op_ld##width##_raw, \ -} -#define OP_ST_TABLE(width) \ -static GenOpFunc *gen_op_st##width[] = { \ - &gen_op_st##width##_raw, \ -} -#else -#define OP_LD_TABLE(width) \ -static GenOpFunc *gen_op_ld##width[] = { \ - &gen_op_ld##width##_kernel, \ - &gen_op_ld##width##_executive, \ - &gen_op_ld##width##_supervisor, \ - &gen_op_ld##width##_user, \ -} -#define OP_ST_TABLE(width) \ -static GenOpFunc *gen_op_st##width[] = { \ - &gen_op_st##width##_kernel, \ - &gen_op_st##width##_executive, \ - &gen_op_st##width##_supervisor, \ - &gen_op_st##width##_user, \ +static always_inline void gen_qemu_ldf (TCGv t0, TCGv t1, int flags) +{ + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_qemu_ld32u(tmp, t1, flags); + tcg_gen_helper_1_1(helper_memory_to_f, t0, tmp); + tcg_temp_free(tmp); } -#endif -#define GEN_LD(width) \ -OP_LD_TABLE(width); \ -static always_inline void gen_ld##width (DisasContext *ctx) \ -{ \ - (*gen_op_ld##width[ctx->mem_idx])(); \ -} - -#define GEN_ST(width) \ -OP_ST_TABLE(width); \ -static always_inline void gen_st##width (DisasContext *ctx) \ -{ \ - (*gen_op_st##width[ctx->mem_idx])(); \ -} - -GEN_LD(l); -GEN_ST(l); -GEN_LD(q); -GEN_ST(q); -GEN_LD(l_l); -GEN_ST(l_c); -GEN_LD(q_l); -GEN_ST(q_c); - -#if 0 /* currently unused */ -GEN_LD(f); -GEN_ST(f); -GEN_LD(g); -GEN_ST(g); -#endif /* 0 */ -GEN_LD(s); -GEN_ST(s); -GEN_LD(t); -GEN_ST(t); - -static always_inline void _gen_op_bcond (DisasContext *ctx) +static always_inline void gen_qemu_ldg (TCGv t0, TCGv t1, int flags) { -#if 0 // Qemu does not know how to do this... - gen_op_bcond(ctx->pc); -#else - gen_op_bcond(ctx->pc >> 32, ctx->pc); -#endif + TCGv tmp = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_qemu_ld64(tmp, t1, flags); + tcg_gen_helper_1_1(helper_memory_to_g, t0, tmp); + tcg_temp_free(tmp); } -static always_inline void gen_excp (DisasContext *ctx, - int exception, int error_code) +static always_inline void gen_qemu_lds (TCGv t0, TCGv t1, int flags) { - TCGv tmp1, tmp2; + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_qemu_ld32u(tmp, t1, flags); + tcg_gen_helper_1_1(helper_memory_to_s, t0, tmp); + tcg_temp_free(tmp); +} - tcg_gen_movi_i64(cpu_pc, ctx->pc); - tmp1 = tcg_const_i32(exception); - tmp2 = tcg_const_i32(error_code); - tcg_gen_helper_0_2(helper_excp, tmp1, tmp2); - tcg_temp_free(tmp2); - tcg_temp_free(tmp1); +static always_inline void gen_qemu_ldl_l (TCGv t0, TCGv t1, int flags) +{ + tcg_gen_mov_i64(cpu_lock, t1); + tcg_gen_qemu_ld32s(t0, t1, flags); } -static always_inline void gen_invalid (DisasContext *ctx) +static always_inline void gen_qemu_ldq_l (TCGv t0, TCGv t1, int flags) { - gen_excp(ctx, EXCP_OPCDEC, 0); + tcg_gen_mov_i64(cpu_lock, t1); + tcg_gen_qemu_ld64(t0, t1, flags); } static always_inline void gen_load_mem (DisasContext *ctx, - void (*gen_load_op)(DisasContext *ctx), + void (*tcg_gen_qemu_load)(TCGv t0, TCGv t1, int flags), int ra, int rb, int32_t disp16, - int clear) + int fp, int clear) { - if (ra == 31 && disp16 == 0) { - /* UNOP */ - gen_op_nop(); + TCGv addr; + + if (unlikely(ra == 31)) + return; + + addr = tcg_temp_new(TCG_TYPE_I64); + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); + if (clear) + tcg_gen_andi_i64(addr, addr, ~0x7); } else { - if (rb != 31) - tcg_gen_addi_i64(cpu_T[0], cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(cpu_T[0], disp16); if (clear) - tcg_gen_andi_i64(cpu_T[0], cpu_T[0], ~0x7); - (*gen_load_op)(ctx); - if (ra != 31) - tcg_gen_mov_i64(cpu_ir[ra], cpu_T[1]); + disp16 &= ~0x7; + tcg_gen_movi_i64(addr, disp16); } + if (fp) + tcg_gen_qemu_load(cpu_fir[ra], addr, ctx->mem_idx); + else + tcg_gen_qemu_load(cpu_ir[ra], addr, ctx->mem_idx); + tcg_temp_free(addr); } -static always_inline void gen_store_mem (DisasContext *ctx, - void (*gen_store_op)(DisasContext *ctx), - int ra, int rb, int32_t disp16, - int clear) +static always_inline void gen_qemu_stf (TCGv t0, TCGv t1, int flags) { - if (rb != 31) - tcg_gen_addi_i64(cpu_T[0], cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(cpu_T[0], disp16); - if (clear) - tcg_gen_andi_i64(cpu_T[0], cpu_T[0], ~0x7); - if (ra != 31) - tcg_gen_mov_i64(cpu_T[1], cpu_ir[ra]); - else - tcg_gen_movi_i64(cpu_T[1], 0); - (*gen_store_op)(ctx); + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_helper_1_1(helper_f_to_memory, tmp, t0); + tcg_gen_qemu_st32(tmp, t1, flags); + tcg_temp_free(tmp); } -static always_inline void gen_load_fmem (DisasContext *ctx, - void (*gen_load_fop)(DisasContext *ctx), - int ra, int rb, int32_t disp16) +static always_inline void gen_qemu_stg (TCGv t0, TCGv t1, int flags) { - if (rb != 31) - tcg_gen_addi_i64(cpu_T[0], cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(cpu_T[0], disp16); - (*gen_load_fop)(ctx); - gen_store_fir(ctx, ra, 1); + TCGv tmp = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_helper_1_1(helper_g_to_memory, tmp, t0); + tcg_gen_qemu_st64(tmp, t1, flags); + tcg_temp_free(tmp); } -static always_inline void gen_store_fmem (DisasContext *ctx, - void (*gen_store_fop)(DisasContext *ctx), - int ra, int rb, int32_t disp16) +static always_inline void gen_qemu_sts (TCGv t0, TCGv t1, int flags) { - if (rb != 31) - tcg_gen_addi_i64(cpu_T[0], cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(cpu_T[0], disp16); - gen_load_fir(ctx, ra, 1); - (*gen_store_fop)(ctx); + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_helper_1_1(helper_s_to_memory, tmp, t0); + tcg_gen_qemu_st32(tmp, t1, flags); + tcg_temp_free(tmp); +} + +static always_inline void gen_qemu_stl_c (TCGv t0, TCGv t1, int flags) +{ + int l1, l2; + + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1); + tcg_gen_qemu_st32(t0, t1, flags); + tcg_gen_movi_i64(t0, 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i64(t0, 1); + gen_set_label(l2); + tcg_gen_movi_i64(cpu_lock, -1); +} + +static always_inline void gen_qemu_stq_c (TCGv t0, TCGv t1, int flags) +{ + int l1, l2; + + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1); + tcg_gen_qemu_st64(t0, t1, flags); + tcg_gen_movi_i64(t0, 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i64(t0, 1); + gen_set_label(l2); + tcg_gen_movi_i64(cpu_lock, -1); +} + +static always_inline void gen_store_mem (DisasContext *ctx, + void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, int flags), + int ra, int rb, int32_t disp16, + int fp, int clear) +{ + TCGv addr = tcg_temp_new(TCG_TYPE_I64); + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); + if (clear) + tcg_gen_andi_i64(addr, addr, ~0x7); + } else { + if (clear) + disp16 &= ~0x7; + tcg_gen_movi_i64(addr, disp16); + } + if (ra != 31) { + if (fp) + tcg_gen_qemu_store(cpu_fir[ra], addr, ctx->mem_idx); + else + tcg_gen_qemu_store(cpu_ir[ra], addr, ctx->mem_idx); + } else { + TCGv zero = tcg_const_i64(0); + tcg_gen_qemu_store(zero, addr, ctx->mem_idx); + tcg_temp_free(zero); + } + tcg_temp_free(addr); } static always_inline void gen_bcond (DisasContext *ctx, @@ -349,17 +289,30 @@ } static always_inline void gen_fbcond (DisasContext *ctx, - void (*gen_test_op)(void), + void* func, int ra, int32_t disp16) { - tcg_gen_movi_i64(cpu_T[1], ctx->pc + (int64_t)(disp16 << 2)); - gen_load_fir(ctx, ra, 0); - (*gen_test_op)(); - _gen_op_bcond(ctx); + int l1, l2; + TCGv tmp; + + l1 = gen_new_label(); + l2 = gen_new_label(); + if (ra != 31) { + tmp = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_helper_1_1(func, tmp, cpu_fir[ra]); + } else { + tmp = tcg_const_i64(0); + tcg_gen_helper_1_1(func, tmp, tmp); + } + tcg_gen_brcondi_i64(TCG_COND_NE, tmp, 0, l1); + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i64(cpu_pc, ctx->pc + (int64_t)(disp16 << 2)); + gen_set_label(l2); } -static always_inline void gen_cmov (DisasContext *ctx, - TCGCond inv_cond, +static always_inline void gen_cmov (TCGCond inv_cond, int ra, int rb, int rc, int islit, uint8_t lit, int mask) { @@ -392,55 +345,69 @@ gen_set_label(l1); } -static always_inline void gen_farith2 (DisasContext *ctx, - void (*gen_arith_fop)(void), +static always_inline void gen_farith2 (void *helper, int rb, int rc) { - gen_load_fir(ctx, rb, 0); - (*gen_arith_fop)(); - gen_store_fir(ctx, rc, 0); + if (unlikely(rc == 31)) + return; + + if (rb != 31) + tcg_gen_helper_1_1(helper, cpu_fir[rc], cpu_fir[rb]); + else { + TCGv tmp = tcg_const_i64(0); + tcg_gen_helper_1_1(helper, cpu_fir[rc], tmp); + tcg_temp_free(tmp); + } } -static always_inline void gen_farith3 (DisasContext *ctx, - void (*gen_arith_fop)(void), +static always_inline void gen_farith3 (void *helper, int ra, int rb, int rc) { - gen_load_fir(ctx, ra, 0); - gen_load_fir(ctx, rb, 1); - (*gen_arith_fop)(); - gen_store_fir(ctx, rc, 0); + if (unlikely(rc == 31)) + return; + + if (ra != 31) { + if (rb != 31) + tcg_gen_helper_1_2(helper, cpu_fir[rc], cpu_fir[ra], cpu_fir[rb]); + else { + TCGv tmp = tcg_const_i64(0); + tcg_gen_helper_1_2(helper, cpu_fir[rc], cpu_fir[ra], tmp); + tcg_temp_free(tmp); + } + } else { + TCGv tmp = tcg_const_i64(0); + if (rb != 31) + tcg_gen_helper_1_2(helper, cpu_fir[rc], tmp, cpu_fir[rb]); + else + tcg_gen_helper_1_2(helper, cpu_fir[rc], tmp, tmp); + tcg_temp_free(tmp); + } } -static always_inline void gen_fcmov (DisasContext *ctx, - void (*gen_test_fop)(void), +static always_inline void gen_fcmov (void *func, int ra, int rb, int rc) { - gen_load_fir(ctx, ra, 0); - gen_load_fir(ctx, rb, 1); - (*gen_test_fop)(); - gen_op_cmov_fir(rc); -} + int l1; + TCGv tmp; -static always_inline void gen_fti (DisasContext *ctx, - void (*gen_move_fop)(void), - int ra, int rc) -{ - gen_load_fir(ctx, rc, 0); - (*gen_move_fop)(); - if (ra != 31) - tcg_gen_mov_i64(cpu_ir[ra], cpu_T[0]); -} + if (unlikely(rc == 31)) + return; -static always_inline void gen_itf (DisasContext *ctx, - void (*gen_move_fop)(void), - int ra, int rc) -{ - if (ra != 31) - tcg_gen_mov_i64(cpu_T[0], cpu_ir[ra]); + l1 = gen_new_label(); + tmp = tcg_temp_new(TCG_TYPE_I64); + if (ra != 31) { + tmp = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_helper_1_1(func, tmp, cpu_fir[ra]); + } else { + tmp = tcg_const_i64(0); + tcg_gen_helper_1_1(func, tmp, tmp); + } + tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); + if (rb != 31) + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); else - tcg_gen_movi_i64(cpu_T[0], 0); - (*gen_move_fop)(); - gen_store_fir(ctx, rc, 0); + tcg_gen_movi_i64(cpu_fir[rc], 0); + gen_set_label(l1); } /* EXTWH, EXTWH, EXTLH, EXTQH */ @@ -457,7 +424,7 @@ tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], 64 - ((lit & 7) * 8)); else tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[ra]); - } else { + } else { TCGv tmp1, tmp2; tmp1 = tcg_temp_new(TCG_TYPE_I64); tcg_gen_andi_i64(tmp1, cpu_ir[rb], 7); @@ -491,7 +458,7 @@ tcg_gen_shli_i64(tmp, tmp, 3); tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], tmp); tcg_temp_free(tmp); - } + } if (tcg_gen_ext_i64) tcg_gen_ext_i64(cpu_ir[rc], cpu_ir[rc]); } else @@ -499,9 +466,9 @@ } /* Code to call arith3 helpers */ -static always_inline void gen_arith3_helper(void *helper, - int ra, int rb, int rc, - int islit, uint8_t lit) +static always_inline void gen_arith3 (void *helper, + int ra, int rb, int rc, + int islit, uint8_t lit) { if (unlikely(rc == 31)) return; @@ -655,103 +622,29 @@ /* LDBU */ if (!(ctx->amask & AMASK_BWX)) goto invalid_opc; - if (likely(ra != 31)) { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - tcg_gen_qemu_ld8u(cpu_ir[ra], addr, ctx->mem_idx); - tcg_temp_free(addr); - } + gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0); break; case 0x0B: /* LDQ_U */ - if (likely(ra != 31)) { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) { - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - tcg_gen_andi_i64(addr, addr, ~0x7); - } else - tcg_gen_movi_i64(addr, disp16 & ~0x7); - tcg_gen_qemu_ld64(cpu_ir[ra], addr, ctx->mem_idx); - tcg_temp_free(addr); - } + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 1); break; case 0x0C: /* LDWU */ if (!(ctx->amask & AMASK_BWX)) goto invalid_opc; - if (likely(ra != 31)) { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - tcg_gen_qemu_ld16u(cpu_ir[ra], addr, ctx->mem_idx); - tcg_temp_free(addr); - } + gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 1); break; case 0x0D: /* STW */ - { - TCGv addr; - if (!(ctx->amask & AMASK_BWX)) - goto invalid_opc; - addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - if (ra != 31) - tcg_gen_qemu_st16(cpu_ir[ra], addr, ctx->mem_idx); - else { - TCGv zero = tcg_const_i64(0); - tcg_gen_qemu_st16(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); - } - tcg_temp_free(addr); - } + gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0); break; case 0x0E: /* STB */ - { - TCGv addr; - if (!(ctx->amask & AMASK_BWX)) - goto invalid_opc; - addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - if (ra != 31) - tcg_gen_qemu_st8(cpu_ir[ra], addr, ctx->mem_idx); - else { - TCGv zero = tcg_const_i64(0); - tcg_gen_qemu_st8(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); - } - tcg_temp_free(addr); - } + gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0); break; case 0x0F: /* STQ_U */ - { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) { - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - tcg_gen_andi_i64(addr, addr, ~0x7); - } else - tcg_gen_movi_i64(addr, disp16 & ~0x7); - if (ra != 31) - tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx); - else { - TCGv zero = tcg_const_i64(0); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); - } - tcg_temp_free(addr); - } + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1); break; case 0x10: switch (fn7) { @@ -836,7 +729,7 @@ break; case 0x0F: /* CMPBGE */ - gen_arith3_helper(helper_cmpbge, ra, rb, rc, islit, lit); + gen_arith3(helper_cmpbge, ra, rb, rc, islit, lit); break; case 0x12: /* S8ADDL */ @@ -1002,11 +895,11 @@ break; case 0x40: /* ADDL/V */ - gen_arith3_helper(helper_addlv, ra, rb, rc, islit, lit); + gen_arith3(helper_addlv, ra, rb, rc, islit, lit); break; case 0x49: /* SUBL/V */ - gen_arith3_helper(helper_sublv, ra, rb, rc, islit, lit); + gen_arith3(helper_sublv, ra, rb, rc, islit, lit); break; case 0x4D: /* CMPLT */ @@ -1014,11 +907,11 @@ break; case 0x60: /* ADDQ/V */ - gen_arith3_helper(helper_addqv, ra, rb, rc, islit, lit); + gen_arith3(helper_addqv, ra, rb, rc, islit, lit); break; case 0x69: /* SUBQ/V */ - gen_arith3_helper(helper_subqv, ra, rb, rc, islit, lit); + gen_arith3(helper_subqv, ra, rb, rc, islit, lit); break; case 0x6D: /* CMPLE */ @@ -1059,11 +952,11 @@ break; case 0x14: /* CMOVLBS */ - gen_cmov(ctx, TCG_COND_EQ, ra, rb, rc, islit, lit, 1); + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 1); break; case 0x16: /* CMOVLBC */ - gen_cmov(ctx, TCG_COND_NE, ra, rb, rc, islit, lit, 1); + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 1); break; case 0x20: /* BIS */ @@ -1071,7 +964,7 @@ if (ra != 31) { if (islit) tcg_gen_ori_i64(cpu_ir[rc], cpu_ir[ra], lit); - else + else tcg_gen_or_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); } else { if (islit) @@ -1083,11 +976,11 @@ break; case 0x24: /* CMOVEQ */ - gen_cmov(ctx, TCG_COND_NE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 0); break; case 0x26: /* CMOVNE */ - gen_cmov(ctx, TCG_COND_EQ, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 0); break; case 0x28: /* ORNOT */ @@ -1127,11 +1020,11 @@ break; case 0x44: /* CMOVLT */ - gen_cmov(ctx, TCG_COND_GE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_GE, ra, rb, rc, islit, lit, 0); break; case 0x46: /* CMOVGE */ - gen_cmov(ctx, TCG_COND_LT, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_LT, ra, rb, rc, islit, lit, 0); break; case 0x48: /* EQV */ @@ -1164,11 +1057,11 @@ break; case 0x64: /* CMOVLE */ - gen_cmov(ctx, TCG_COND_GT, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_GT, ra, rb, rc, islit, lit, 0); break; case 0x66: /* CMOVGT */ - gen_cmov(ctx, TCG_COND_LE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_LE, ra, rb, rc, islit, lit, 0); break; case 0x6C: /* IMPLVER */ @@ -1183,7 +1076,7 @@ switch (fn7) { case 0x02: /* MSKBL */ - gen_arith3_helper(helper_mskbl, ra, rb, rc, islit, lit); + gen_arith3(helper_mskbl, ra, rb, rc, islit, lit); break; case 0x06: /* EXTBL */ @@ -1191,11 +1084,11 @@ break; case 0x0B: /* INSBL */ - gen_arith3_helper(helper_insbl, ra, rb, rc, islit, lit); + gen_arith3(helper_insbl, ra, rb, rc, islit, lit); break; case 0x12: /* MSKWL */ - gen_arith3_helper(helper_mskwl, ra, rb, rc, islit, lit); + gen_arith3(helper_mskwl, ra, rb, rc, islit, lit); break; case 0x16: /* EXTWL */ @@ -1203,11 +1096,11 @@ break; case 0x1B: /* INSWL */ - gen_arith3_helper(helper_inswl, ra, rb, rc, islit, lit); + gen_arith3(helper_inswl, ra, rb, rc, islit, lit); break; case 0x22: /* MSKLL */ - gen_arith3_helper(helper_mskll, ra, rb, rc, islit, lit); + gen_arith3(helper_mskll, ra, rb, rc, islit, lit); break; case 0x26: /* EXTLL */ @@ -1215,19 +1108,19 @@ break; case 0x2B: /* INSLL */ - gen_arith3_helper(helper_insll, ra, rb, rc, islit, lit); + gen_arith3(helper_insll, ra, rb, rc, islit, lit); break; case 0x30: /* ZAP */ - gen_arith3_helper(helper_zap, ra, rb, rc, islit, lit); + gen_arith3(helper_zap, ra, rb, rc, islit, lit); break; case 0x31: /* ZAPNOT */ - gen_arith3_helper(helper_zapnot, ra, rb, rc, islit, lit); + gen_arith3(helper_zapnot, ra, rb, rc, islit, lit); break; case 0x32: /* MSKQL */ - gen_arith3_helper(helper_mskql, ra, rb, rc, islit, lit); + gen_arith3(helper_mskql, ra, rb, rc, islit, lit); break; case 0x34: /* SRL */ @@ -1267,7 +1160,7 @@ break; case 0x3B: /* INSQL */ - gen_arith3_helper(helper_insql, ra, rb, rc, islit, lit); + gen_arith3(helper_insql, ra, rb, rc, islit, lit); break; case 0x3C: /* SRA */ @@ -1287,11 +1180,11 @@ break; case 0x52: /* MSKWH */ - gen_arith3_helper(helper_mskwh, ra, rb, rc, islit, lit); + gen_arith3(helper_mskwh, ra, rb, rc, islit, lit); break; case 0x57: /* INSWH */ - gen_arith3_helper(helper_inswh, ra, rb, rc, islit, lit); + gen_arith3(helper_inswh, ra, rb, rc, islit, lit); break; case 0x5A: /* EXTWH */ @@ -1299,11 +1192,11 @@ break; case 0x62: /* MSKLH */ - gen_arith3_helper(helper_msklh, ra, rb, rc, islit, lit); + gen_arith3(helper_msklh, ra, rb, rc, islit, lit); break; case 0x67: /* INSLH */ - gen_arith3_helper(helper_inslh, ra, rb, rc, islit, lit); + gen_arith3(helper_inslh, ra, rb, rc, islit, lit); break; case 0x6A: /* EXTLH */ @@ -1311,11 +1204,11 @@ break; case 0x72: /* MSKQH */ - gen_arith3_helper(helper_mskqh, ra, rb, rc, islit, lit); + gen_arith3(helper_mskqh, ra, rb, rc, islit, lit); break; case 0x77: /* INSQH */ - gen_arith3_helper(helper_insqh, ra, rb, rc, islit, lit); + gen_arith3(helper_insqh, ra, rb, rc, islit, lit); break; case 0x7A: /* EXTQH */ @@ -1354,15 +1247,15 @@ break; case 0x30: /* UMULH */ - gen_arith3_helper(helper_umulh, ra, rb, rc, islit, lit); + gen_arith3(helper_umulh, ra, rb, rc, islit, lit); break; case 0x40: /* MULL/V */ - gen_arith3_helper(helper_mullv, ra, rb, rc, islit, lit); + gen_arith3(helper_mullv, ra, rb, rc, islit, lit); break; case 0x60: /* MULQ/V */ - gen_arith3_helper(helper_mulqv, ra, rb, rc, islit, lit); + gen_arith3(helper_mulqv, ra, rb, rc, islit, lit); break; default: goto invalid_opc; @@ -1374,47 +1267,64 @@ /* ITOFS */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_itf(ctx, &gen_op_itofs, ra, rc); + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]); + tcg_gen_helper_1_1(helper_memory_to_s, cpu_fir[rc], tmp); + tcg_temp_free(tmp); + } else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } break; case 0x0A: /* SQRTF */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_farith2(ctx, &gen_op_sqrtf, rb, rc); + gen_farith2(&helper_sqrtf, rb, rc); break; case 0x0B: /* SQRTS */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_farith2(ctx, &gen_op_sqrts, rb, rc); + gen_farith2(&helper_sqrts, rb, rc); break; case 0x14: /* ITOFF */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; -#if 0 // TODO - gen_itf(ctx, &gen_op_itoff, ra, rc); -#else - goto invalid_opc; -#endif + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(TCG_TYPE_I32); + tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]); + tcg_gen_helper_1_1(helper_memory_to_f, cpu_fir[rc], tmp); + tcg_temp_free(tmp); + } else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } break; case 0x24: /* ITOFT */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_itf(ctx, &gen_op_itoft, ra, rc); + if (likely(rc != 31)) { + if (ra != 31) + tcg_gen_mov_i64(cpu_fir[rc], cpu_ir[ra]); + else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } break; case 0x2A: /* SQRTG */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_farith2(ctx, &gen_op_sqrtg, rb, rc); + gen_farith2(&helper_sqrtg, rb, rc); break; case 0x02B: /* SQRTT */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_farith2(ctx, &gen_op_sqrtt, rb, rc); + gen_farith2(&helper_sqrtt, rb, rc); break; default: goto invalid_opc; @@ -1426,79 +1336,79 @@ switch (fpfn) { /* f11 & 0x3F */ case 0x00: /* ADDF */ - gen_farith3(ctx, &gen_op_addf, ra, rb, rc); + gen_farith3(&helper_addf, ra, rb, rc); break; case 0x01: /* SUBF */ - gen_farith3(ctx, &gen_op_subf, ra, rb, rc); + gen_farith3(&helper_subf, ra, rb, rc); break; case 0x02: /* MULF */ - gen_farith3(ctx, &gen_op_mulf, ra, rb, rc); + gen_farith3(&helper_mulf, ra, rb, rc); break; case 0x03: /* DIVF */ - gen_farith3(ctx, &gen_op_divf, ra, rb, rc); + gen_farith3(&helper_divf, ra, rb, rc); break; case 0x1E: /* CVTDG */ #if 0 // TODO - gen_farith2(ctx, &gen_op_cvtdg, rb, rc); + gen_farith2(&helper_cvtdg, rb, rc); #else goto invalid_opc; #endif break; case 0x20: /* ADDG */ - gen_farith3(ctx, &gen_op_addg, ra, rb, rc); + gen_farith3(&helper_addg, ra, rb, rc); break; case 0x21: /* SUBG */ - gen_farith3(ctx, &gen_op_subg, ra, rb, rc); + gen_farith3(&helper_subg, ra, rb, rc); break; case 0x22: /* MULG */ - gen_farith3(ctx, &gen_op_mulg, ra, rb, rc); + gen_farith3(&helper_mulg, ra, rb, rc); break; case 0x23: /* DIVG */ - gen_farith3(ctx, &gen_op_divg, ra, rb, rc); + gen_farith3(&helper_divg, ra, rb, rc); break; case 0x25: /* CMPGEQ */ - gen_farith3(ctx, &gen_op_cmpgeq, ra, rb, rc); + gen_farith3(&helper_cmpgeq, ra, rb, rc); break; case 0x26: /* CMPGLT */ - gen_farith3(ctx, &gen_op_cmpglt, ra, rb, rc); + gen_farith3(&helper_cmpglt, ra, rb, rc); break; case 0x27: /* CMPGLE */ - gen_farith3(ctx, &gen_op_cmpgle, ra, rb, rc); + gen_farith3(&helper_cmpgle, ra, rb, rc); break; case 0x2C: /* CVTGF */ - gen_farith2(ctx, &gen_op_cvtgf, rb, rc); + gen_farith2(&helper_cvtgf, rb, rc); break; case 0x2D: /* CVTGD */ #if 0 // TODO - gen_farith2(ctx, &gen_op_cvtgd, rb, rc); + gen_farith2(ctx, &helper_cvtgd, rb, rc); #else goto invalid_opc; #endif break; case 0x2F: /* CVTGQ */ - gen_farith2(ctx, &gen_op_cvtgq, rb, rc); + gen_farith2(&helper_cvtgq, rb, rc); break; case 0x3C: /* CVTQF */ - gen_farith2(ctx, &gen_op_cvtqf, rb, rc); + gen_farith2(&helper_cvtqf, rb, rc); break; case 0x3E: /* CVTQG */ - gen_farith2(ctx, &gen_op_cvtqg, rb, rc); + gen_farith2(&helper_cvtqg, rb, rc); break; default: goto invalid_opc; @@ -1510,73 +1420,73 @@ switch (fpfn) { /* f11 & 0x3F */ case 0x00: /* ADDS */ - gen_farith3(ctx, &gen_op_adds, ra, rb, rc); + gen_farith3(&helper_adds, ra, rb, rc); break; case 0x01: /* SUBS */ - gen_farith3(ctx, &gen_op_subs, ra, rb, rc); + gen_farith3(&helper_subs, ra, rb, rc); break; case 0x02: /* MULS */ - gen_farith3(ctx, &gen_op_muls, ra, rb, rc); + gen_farith3(&helper_muls, ra, rb, rc); break; case 0x03: /* DIVS */ - gen_farith3(ctx, &gen_op_divs, ra, rb, rc); + gen_farith3(&helper_divs, ra, rb, rc); break; case 0x20: /* ADDT */ - gen_farith3(ctx, &gen_op_addt, ra, rb, rc); + gen_farith3(&helper_addt, ra, rb, rc); break; case 0x21: /* SUBT */ - gen_farith3(ctx, &gen_op_subt, ra, rb, rc); + gen_farith3(&helper_subt, ra, rb, rc); break; case 0x22: /* MULT */ - gen_farith3(ctx, &gen_op_mult, ra, rb, rc); + gen_farith3(&helper_mult, ra, rb, rc); break; case 0x23: /* DIVT */ - gen_farith3(ctx, &gen_op_divt, ra, rb, rc); + gen_farith3(&helper_divt, ra, rb, rc); break; case 0x24: /* CMPTUN */ - gen_farith3(ctx, &gen_op_cmptun, ra, rb, rc); + gen_farith3(&helper_cmptun, ra, rb, rc); break; case 0x25: /* CMPTEQ */ - gen_farith3(ctx, &gen_op_cmpteq, ra, rb, rc); + gen_farith3(&helper_cmpteq, ra, rb, rc); break; case 0x26: /* CMPTLT */ - gen_farith3(ctx, &gen_op_cmptlt, ra, rb, rc); + gen_farith3(&helper_cmptlt, ra, rb, rc); break; case 0x27: /* CMPTLE */ - gen_farith3(ctx, &gen_op_cmptle, ra, rb, rc); + gen_farith3(&helper_cmptle, ra, rb, rc); break; case 0x2C: /* XXX: incorrect */ if (fn11 == 0x2AC) { /* CVTST */ - gen_farith2(ctx, &gen_op_cvtst, rb, rc); + gen_farith2(&helper_cvtst, rb, rc); } else { /* CVTTS */ - gen_farith2(ctx, &gen_op_cvtts, rb, rc); + gen_farith2(&helper_cvtts, rb, rc); } break; case 0x2F: /* CVTTQ */ - gen_farith2(ctx, &gen_op_cvttq, rb, rc); + gen_farith2(&helper_cvttq, rb, rc); break; case 0x3C: /* CVTQS */ - gen_farith2(ctx, &gen_op_cvtqs, rb, rc); + gen_farith2(&helper_cvtqs, rb, rc); break; case 0x3E: /* CVTQT */ - gen_farith2(ctx, &gen_op_cvtqt, rb, rc); + gen_farith2(&helper_cvtqt, rb, rc); break; default: goto invalid_opc; @@ -1586,76 +1496,76 @@ switch (fn11) { case 0x010: /* CVTLQ */ - gen_farith2(ctx, &gen_op_cvtlq, rb, rc); + gen_farith2(&helper_cvtlq, rb, rc); break; case 0x020: - /* CPYS */ - if (ra == rb) { - if (ra == 31 && rc == 31) { - /* FNOP */ - gen_op_nop(); - } else { + if (likely(rc != 31)) { + if (ra == rb) /* FMOV */ - gen_load_fir(ctx, rb, 0); - gen_store_fir(ctx, rc, 0); - } - } else { - gen_farith3(ctx, &gen_op_cpys, ra, rb, rc); + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); + else + /* CPYS */ + gen_farith3(&helper_cpys, ra, rb, rc); } break; case 0x021: /* CPYSN */ - gen_farith2(ctx, &gen_op_cpysn, rb, rc); + gen_farith3(&helper_cpysn, ra, rb, rc); break; case 0x022: /* CPYSE */ - gen_farith2(ctx, &gen_op_cpyse, rb, rc); + gen_farith3(&helper_cpyse, ra, rb, rc); break; case 0x024: /* MT_FPCR */ - gen_load_fir(ctx, ra, 0); - gen_op_store_fpcr(); + if (likely(ra != 31)) + tcg_gen_helper_0_1(helper_store_fpcr, cpu_fir[ra]); + else { + TCGv tmp = tcg_const_i64(0); + tcg_gen_helper_0_1(helper_store_fpcr, tmp); + tcg_temp_free(tmp); + } break; case 0x025: /* MF_FPCR */ - gen_op_load_fpcr(); - gen_store_fir(ctx, ra, 0); + if (likely(ra != 31)) + tcg_gen_helper_1_0(helper_load_fpcr, cpu_fir[ra]); break; case 0x02A: /* FCMOVEQ */ - gen_fcmov(ctx, &gen_op_cmpfeq, ra, rb, rc); + gen_fcmov(&helper_cmpfeq, ra, rb, rc); break; case 0x02B: /* FCMOVNE */ - gen_fcmov(ctx, &gen_op_cmpfne, ra, rb, rc); + gen_fcmov(&helper_cmpfne, ra, rb, rc); break; case 0x02C: /* FCMOVLT */ - gen_fcmov(ctx, &gen_op_cmpflt, ra, rb, rc); + gen_fcmov(&helper_cmpflt, ra, rb, rc); break; case 0x02D: /* FCMOVGE */ - gen_fcmov(ctx, &gen_op_cmpfge, ra, rb, rc); + gen_fcmov(&helper_cmpfge, ra, rb, rc); break; case 0x02E: /* FCMOVLE */ - gen_fcmov(ctx, &gen_op_cmpfle, ra, rb, rc); + gen_fcmov(&helper_cmpfle, ra, rb, rc); break; case 0x02F: /* FCMOVGT */ - gen_fcmov(ctx, &gen_op_cmpfgt, ra, rb, rc); + gen_fcmov(&helper_cmpfgt, ra, rb, rc); break; case 0x030: /* CVTQL */ - gen_farith2(ctx, &gen_op_cvtql, rb, rc); + gen_farith2(&helper_cvtql, rb, rc); break; case 0x130: /* CVTQL/V */ - gen_farith2(ctx, &gen_op_cvtqlv, rb, rc); + gen_farith2(&helper_cvtqlv, rb, rc); break; case 0x530: /* CVTQL/SV */ - gen_farith2(ctx, &gen_op_cvtqlsv, rb, rc); + gen_farith2(&helper_cvtqlsv, rb, rc); break; default: goto invalid_opc; @@ -1728,9 +1638,11 @@ #else if (!ctx->pal_mode) goto invalid_opc; - gen_op_mfpr(insn & 0xFF); - if (ra != 31) - tcg_gen_mov_i64(cpu_ir[ra], cpu_T[0]); + if (ra != 31) { + TCGv tmp = tcg_const_i32(insn & 0xFF); + tcg_gen_helper_1_2(helper_mfpr, cpu_ir[ra], tmp, cpu_ir[ra]); + tcg_temp_free(tmp); + } break; #endif case 0x1A: @@ -1764,94 +1676,94 @@ #else if (!ctx->pal_mode) goto invalid_opc; - if (rb != 31) - tcg_gen_mov_i64(cpu_T[0], cpu_ir[rb]); - else - tcg_gen_movi_i64(cpu_T[0], 0); - tcg_gen_movi_i64(cpu_T[1], disp12); - tcg_gen_add_i64(cpu_T[0], cpu_T[0], cpu_T[1]); - switch ((insn >> 12) & 0xF) { - case 0x0: - /* Longword physical access */ - gen_op_ldl_raw(); - break; - case 0x1: - /* Quadword physical access */ - gen_op_ldq_raw(); - break; - case 0x2: - /* Longword physical access with lock */ - gen_op_ldl_l_raw(); - break; - case 0x3: - /* Quadword physical access with lock */ - gen_op_ldq_l_raw(); - break; - case 0x4: - /* Longword virtual PTE fetch */ - gen_op_ldl_kernel(); - break; - case 0x5: - /* Quadword virtual PTE fetch */ - gen_op_ldq_kernel(); - break; - case 0x6: - /* Invalid */ - goto invalid_opc; - case 0x7: - /* Invalid */ - goto invalid_opc; - case 0x8: - /* Longword virtual access */ - gen_op_ld_phys_to_virt(); - gen_op_ldl_raw(); - break; - case 0x9: - /* Quadword virtual access */ - gen_op_ld_phys_to_virt(); - gen_op_ldq_raw(); - break; - case 0xA: - /* Longword virtual access with protection check */ - gen_ldl(ctx); - break; - case 0xB: - /* Quadword virtual access with protection check */ - gen_ldq(ctx); - break; - case 0xC: - /* Longword virtual access with altenate access mode */ - gen_op_set_alt_mode(); - gen_op_ld_phys_to_virt(); - gen_op_ldl_raw(); - gen_op_restore_mode(); - break; - case 0xD: - /* Quadword virtual access with altenate access mode */ - gen_op_set_alt_mode(); - gen_op_ld_phys_to_virt(); - gen_op_ldq_raw(); - gen_op_restore_mode(); - break; - case 0xE: - /* Longword virtual access with alternate access mode and - * protection checks - */ - gen_op_set_alt_mode(); - gen_op_ldl_data(); - gen_op_restore_mode(); - break; - case 0xF: - /* Quadword virtual access with alternate access mode and - * protection checks - */ - gen_op_set_alt_mode(); - gen_op_ldq_data(); - gen_op_restore_mode(); - break; + if (ra != 31) { + TCGv addr = tcg_temp_new(TCG_TYPE_I64); + if (rb != 31) + tcg_gen_addi_i64(addr, cpu_ir[rb], disp12); + else + tcg_gen_movi_i64(addr, disp12); + switch ((insn >> 12) & 0xF) { + case 0x0: + /* Longword physical access */ + tcg_gen_helper_0_2(helper_ldl_raw, cpu_ir[ra], addr); + break; + case 0x1: + /* Quadword physical access */ + tcg_gen_helper_0_2(helper_ldq_raw, cpu_ir[ra], addr); + break; + case 0x2: + /* Longword physical access with lock */ + tcg_gen_helper_0_2(helper_ldl_l_raw, cpu_ir[ra], addr); + break; + case 0x3: + /* Quadword physical access with lock */ + tcg_gen_helper_0_2(helper_ldq_l_raw, cpu_ir[ra], addr); + break; + case 0x4: + /* Longword virtual PTE fetch */ + tcg_gen_helper_0_2(helper_ldl_kernel, cpu_ir[ra], addr); + break; + case 0x5: + /* Quadword virtual PTE fetch */ + tcg_gen_helper_0_2(helper_ldq_kernel, cpu_ir[ra], addr); + break; + case 0x6: + /* Incpu_ir[ra]id */ + goto incpu_ir[ra]id_opc; + case 0x7: + /* Incpu_ir[ra]id */ + goto incpu_ir[ra]id_opc; + case 0x8: + /* Longword virtual access */ + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_ldl_raw, cpu_ir[ra], addr); + break; + case 0x9: + /* Quadword virtual access */ + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_ldq_raw, cpu_ir[ra], addr); + break; + case 0xA: + /* Longword virtual access with protection check */ + tcg_gen_qemu_ld32s(cpu_ir[ra], addr, ctx->flags); + break; + case 0xB: + /* Quadword virtual access with protection check */ + tcg_gen_qemu_ld64(cpu_ir[ra], addr, ctx->flags); + break; + case 0xC: + /* Longword virtual access with altenate access mode */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_ldl_raw, cpu_ir[ra], addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + case 0xD: + /* Quadword virtual access with altenate access mode */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_ldq_raw, cpu_ir[ra], addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + case 0xE: + /* Longword virtual access with alternate access mode and + * protection checks + */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_0_2(helper_ldl_data, cpu_ir[ra], addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + case 0xF: + /* Quadword virtual access with alternate access mode and + * protection checks + */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_0_2(helper_ldq_data, cpu_ir[ra], addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + } + tcg_temp_free(addr); } - if (ra != 31) - tcg_gen_mov_i64(cpu_ir[ra], cpu_T[1]); break; #endif case 0x1C: @@ -2006,13 +1918,29 @@ /* FTOIT */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_fti(ctx, &gen_op_ftoit, ra, rb); + if (likely(rc != 31)) { + if (ra != 31) + tcg_gen_mov_i64(cpu_ir[rc], cpu_fir[ra]); + else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } break; case 0x78: /* FTOIS */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_fti(ctx, &gen_op_ftois, ra, rb); + if (rc != 31) { + TCGv tmp1 = tcg_temp_new(TCG_TYPE_I32); + if (ra != 31) + tcg_gen_helper_1_1(helper_s_to_memory, tmp1, cpu_fir[ra]); + else { + TCGv tmp2 = tcg_const_i64(0); + tcg_gen_helper_1_1(helper_s_to_memory, tmp1, tmp2); + tcg_temp_free(tmp2); + } + tcg_gen_ext_i32_i64(cpu_ir[rc], tmp1); + tcg_temp_free(tmp1); + } break; default: goto invalid_opc; @@ -2025,12 +1953,18 @@ #else if (!ctx->pal_mode) goto invalid_opc; - if (ra != 31) - tcg_gen_mov_i64(cpu_T[0], cpu_ir[ra]); - else - tcg_gen_movi_i64(cpu_T[0], 0); - gen_op_mtpr(insn & 0xFF); - ret = 2; + else { + TCGv tmp1 = tcg_const_i32(insn & 0xFF); + if (ra != 31) + tcg_gen_helper(helper_mtpr, tmp1, cpu_ir[ra]); + else { + TCGv tmp2 = tcg_const_i64(0); + tcg_gen_helper(helper_mtpr, tmp1, tmp2); + tcg_temp_free(tmp2); + } + tcg_temp_free(tmp1); + ret = 2; + } break; #endif case 0x1E: @@ -2042,15 +1976,17 @@ goto invalid_opc; if (rb == 31) { /* "Old" alpha */ - gen_op_hw_rei(); + tcg_gen_helper_0_0(helper_hw_rei); } else { - if (ra != 31) - tcg_gen_mov_i64(cpu_T[0], cpu_ir[rb]); - else - tcg_gen_movi_i64(cpu_T[0], 0); - tcg_gen_movi_i64(cpu_T[1], (((int64_t)insn << 51) >> 51)); - tcg_gen_add_i64(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_hw_ret(); + TCGv tmp; + + if (ra != 31) { + tmp = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_addi_i64(tmp, cpu_ir[rb], (((int64_t)insn << 51) >> 51)); + } else + tmp = tcg_const_i64(((int64_t)insn << 51) >> 51); + tcg_gen_helper_0_1(helper_hw_ret, tmp); + tcg_temp_free(tmp); } ret = 2; break; @@ -2062,206 +1998,155 @@ #else if (!ctx->pal_mode) goto invalid_opc; - if (ra != 31) - tcg_gen_addi_i64(cpu_T[0], cpu_ir[rb], disp12); - else - tcg_gen_movi_i64(cpu_T[0], disp12); - if (ra != 31) - tcg_gen_mov_i64(cpu_T[1], cpu_ir[ra]); - else - tcg_gen_movi_i64(cpu_T[1], 0); - switch ((insn >> 12) & 0xF) { - case 0x0: - /* Longword physical access */ - gen_op_stl_raw(); - break; - case 0x1: - /* Quadword physical access */ - gen_op_stq_raw(); - break; - case 0x2: - /* Longword physical access with lock */ - gen_op_stl_c_raw(); - break; - case 0x3: - /* Quadword physical access with lock */ - gen_op_stq_c_raw(); - break; - case 0x4: - /* Longword virtual access */ - gen_op_st_phys_to_virt(); - gen_op_stl_raw(); - break; - case 0x5: - /* Quadword virtual access */ - gen_op_st_phys_to_virt(); - gen_op_stq_raw(); - break; - case 0x6: - /* Invalid */ - goto invalid_opc; - case 0x7: - /* Invalid */ - goto invalid_opc; - case 0x8: - /* Invalid */ - goto invalid_opc; - case 0x9: - /* Invalid */ - goto invalid_opc; - case 0xA: - /* Invalid */ - goto invalid_opc; - case 0xB: - /* Invalid */ - goto invalid_opc; - case 0xC: - /* Longword virtual access with alternate access mode */ - gen_op_set_alt_mode(); - gen_op_st_phys_to_virt(); - gen_op_ldl_raw(); - gen_op_restore_mode(); - break; - case 0xD: - /* Quadword virtual access with alternate access mode */ - gen_op_set_alt_mode(); - gen_op_st_phys_to_virt(); - gen_op_ldq_raw(); - gen_op_restore_mode(); - break; - case 0xE: - /* Invalid */ - goto invalid_opc; - case 0xF: - /* Invalid */ - goto invalid_opc; + else { + TCGv addr, val; + addr = tcg_temp_new(TCG_TYPE_I64); + if (rb != 31) + tcg_gen_addi_i64(addr, cpu_ir[rb], disp12); + else + tcg_gen_movi_i64(addr, disp12); + if (ra != 31) + val = cpu_ir[ra]; + else { + val = tcg_temp_new(TCG_TYPE_I64); + tcg_gen_movi_i64(val, 0); + } + switch ((insn >> 12) & 0xF) { + case 0x0: + /* Longword physical access */ + tcg_gen_helper_0_2(helper_stl_raw, val, addr); + break; + case 0x1: + /* Quadword physical access */ + tcg_gen_helper_0_2(helper_stq_raw, val, addr); + break; + case 0x2: + /* Longword physical access with lock */ + tcg_gen_helper_1_2(helper_stl_c_raw, val, val, addr); + break; + case 0x3: + /* Quadword physical access with lock */ + tcg_gen_helper_1_2(helper_stq_c_raw, val, val, addr); + break; + case 0x4: + /* Longword virtual access */ + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_stl_raw, val, addr); + break; + case 0x5: + /* Quadword virtual access */ + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_stq_raw, val, addr); + break; + case 0x6: + /* Invalid */ + goto invalid_opc; + case 0x7: + /* Invalid */ + goto invalid_opc; + case 0x8: + /* Invalid */ + goto invalid_opc; + case 0x9: + /* Invalid */ + goto invalid_opc; + case 0xA: + /* Invalid */ + goto invalid_opc; + case 0xB: + /* Invalid */ + goto invalid_opc; + case 0xC: + /* Longword virtual access with alternate access mode */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_stl_raw, val, addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + case 0xD: + /* Quadword virtual access with alternate access mode */ + tcg_gen_helper_0_0(helper_set_alt_mode); + tcg_gen_helper_1_1(helper_st_virt_to_phys, addr, addr); + tcg_gen_helper_0_2(helper_stl_raw, val, addr); + tcg_gen_helper_0_0(helper_restore_mode); + break; + case 0xE: + /* Invalid */ + goto invalid_opc; + case 0xF: + /* Invalid */ + goto invalid_opc; + } + if (ra != 31) + tcg_temp_free(val); + tcg_temp_free(addr); } ret = 2; break; #endif case 0x20: /* LDF */ -#if 0 // TODO - gen_load_fmem(ctx, &gen_ldf, ra, rb, disp16); -#else - goto invalid_opc; -#endif + gen_load_mem(ctx, &gen_qemu_ldf, ra, rb, disp16, 1, 0); break; case 0x21: /* LDG */ -#if 0 // TODO - gen_load_fmem(ctx, &gen_ldg, ra, rb, disp16); -#else - goto invalid_opc; -#endif + gen_load_mem(ctx, &gen_qemu_ldg, ra, rb, disp16, 1, 0); break; case 0x22: /* LDS */ - gen_load_fmem(ctx, &gen_lds, ra, rb, disp16); + gen_load_mem(ctx, &gen_qemu_lds, ra, rb, disp16, 1, 0); break; case 0x23: /* LDT */ - gen_load_fmem(ctx, &gen_ldt, ra, rb, disp16); + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 1, 0); break; case 0x24: /* STF */ -#if 0 // TODO - gen_store_fmem(ctx, &gen_stf, ra, rb, disp16); -#else - goto invalid_opc; -#endif + gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0); break; case 0x25: /* STG */ -#if 0 // TODO - gen_store_fmem(ctx, &gen_stg, ra, rb, disp16); -#else - goto invalid_opc; -#endif + gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0); break; case 0x26: /* STS */ - gen_store_fmem(ctx, &gen_sts, ra, rb, disp16); + gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0); break; case 0x27: /* STT */ - gen_store_fmem(ctx, &gen_stt, ra, rb, disp16); + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0); break; case 0x28: /* LDL */ - if (likely(ra != 31)) { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - tcg_gen_qemu_ld32s(cpu_ir[ra], addr, ctx->mem_idx); - tcg_temp_free(addr); - } + gen_load_mem(ctx, &tcg_gen_qemu_ld32s, ra, rb, disp16, 0, 0); break; case 0x29: /* LDQ */ - if (likely(ra != 31)) { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - tcg_gen_qemu_ld64(cpu_ir[ra], addr, ctx->mem_idx); - tcg_temp_free(addr); - } + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 0); break; case 0x2A: /* LDL_L */ - gen_load_mem(ctx, &gen_ldl_l, ra, rb, disp16, 0); + gen_load_mem(ctx, &gen_qemu_ldl_l, ra, rb, disp16, 0, 0); break; case 0x2B: /* LDQ_L */ - gen_load_mem(ctx, &gen_ldq_l, ra, rb, disp16, 0); + gen_load_mem(ctx, &gen_qemu_ldq_l, ra, rb, disp16, 0, 0); break; case 0x2C: /* STL */ - { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - if (ra != 31) - tcg_gen_qemu_st32(cpu_ir[ra], addr, ctx->mem_idx); - else { - TCGv zero = tcg_const_i64(0); - tcg_gen_qemu_st32(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); - } - tcg_temp_free(addr); - } + gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0); break; case 0x2D: /* STQ */ - { - TCGv addr = tcg_temp_new(TCG_TYPE_I64); - if (rb != 31) - tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - else - tcg_gen_movi_i64(addr, disp16); - if (ra != 31) - tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx); - else { - TCGv zero = tcg_const_i64(0); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); - } - tcg_temp_free(addr); - } + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0); break; case 0x2E: /* STL_C */ - gen_store_mem(ctx, &gen_stl_c, ra, rb, disp16, 0); + gen_store_mem(ctx, &gen_qemu_stl_c, ra, rb, disp16, 0, 0); break; case 0x2F: /* STQ_C */ - gen_store_mem(ctx, &gen_stq_c, ra, rb, disp16, 0); + gen_store_mem(ctx, &gen_qemu_stq_c, ra, rb, disp16, 0, 0); break; case 0x30: /* BR */ @@ -2272,17 +2157,17 @@ break; case 0x31: /* FBEQ */ - gen_fbcond(ctx, &gen_op_cmpfeq, ra, disp16); + gen_fbcond(ctx, &helper_cmpfeq, ra, disp16); ret = 1; break; case 0x32: /* FBLT */ - gen_fbcond(ctx, &gen_op_cmpflt, ra, disp16); + gen_fbcond(ctx, &helper_cmpflt, ra, disp16); ret = 1; break; case 0x33: /* FBLE */ - gen_fbcond(ctx, &gen_op_cmpfle, ra, disp16); + gen_fbcond(ctx, &helper_cmpfle, ra, disp16); ret = 1; break; case 0x34: @@ -2294,17 +2179,17 @@ break; case 0x35: /* FBNE */ - gen_fbcond(ctx, &gen_op_cmpfne, ra, disp16); + gen_fbcond(ctx, &helper_cmpfne, ra, disp16); ret = 1; break; case 0x36: /* FBGE */ - gen_fbcond(ctx, &gen_op_cmpfge, ra, disp16); + gen_fbcond(ctx, &helper_cmpfge, ra, disp16); ret = 1; break; case 0x37: /* FBGT */ - gen_fbcond(ctx, &gen_op_cmpfgt, ra, disp16); + gen_fbcond(ctx, &helper_cmpfgt, ra, disp16); ret = 1; break; case 0x38: @@ -2468,7 +2353,7 @@ } if (loglevel & CPU_LOG_TB_IN_ASM) { fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start)); - target_disas(logfile, pc_start, ctx.pc - pc_start, 1); + target_disas(logfile, pc_start, ctx.pc - pc_start, 1); fprintf(logfile, "\n"); } #endif diff -Nur kvm-76/qemu/target-i386/cpu.h kvm-userspace/qemu/target-i386/cpu.h --- kvm-76/qemu/target-i386/cpu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/cpu.h 2008-10-12 09:38:24.000000000 +0200 @@ -248,6 +248,8 @@ #define MSR_MCG_STATUS 0x17a #define MSR_MCG_CTL 0x17b +#define MSR_IA32_PERF_STATUS 0x198 + #define MSR_PAT 0x277 #define MSR_EFER 0xc0000080 @@ -302,6 +304,7 @@ #define CPUID_PBE (1 << 31) #define CPUID_EXT_SSE3 (1 << 0) +#define CPUID_EXT_DTES64 (1 << 2) #define CPUID_EXT_MONITOR (1 << 3) #define CPUID_EXT_DSCPL (1 << 4) #define CPUID_EXT_VMX (1 << 5) @@ -312,8 +315,15 @@ #define CPUID_EXT_CID (1 << 10) #define CPUID_EXT_CX16 (1 << 13) #define CPUID_EXT_XTPR (1 << 14) -#define CPUID_EXT_DCA (1 << 17) -#define CPUID_EXT_POPCNT (1 << 22) +#define CPUID_EXT_PDCM (1 << 15) +#define CPUID_EXT_DCA (1 << 18) +#define CPUID_EXT_SSE41 (1 << 19) +#define CPUID_EXT_SSE42 (1 << 20) +#define CPUID_EXT_X2APIC (1 << 21) +#define CPUID_EXT_MOVBE (1 << 22) +#define CPUID_EXT_POPCNT (1 << 23) +#define CPUID_EXT_XSAVE (1 << 26) +#define CPUID_EXT_OSXSAVE (1 << 27) #define CPUID_EXT2_SYSCALL (1 << 11) #define CPUID_EXT2_MP (1 << 19) @@ -339,6 +349,17 @@ #define CPUID_EXT3_IBS (1 << 10) #define CPUID_EXT3_SKINIT (1 << 12) +#define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ +#define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ +#define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ + +#define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */ +#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */ +#define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ + +#define CPUID_MWAIT_IBE (1 << 1) /* Interrupts can exit capability */ +#define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */ + #define EXCP00_DIVZ 0 #define EXCP01_SSTP 1 #define EXCP02_NMI 2 @@ -542,8 +563,8 @@ /* sysenter registers */ uint32_t sysenter_cs; - uint64_t sysenter_esp; - uint64_t sysenter_eip; + target_ulong sysenter_esp; + target_ulong sysenter_eip; uint64_t efer; uint64_t star; @@ -737,7 +758,7 @@ #define cpu_signal_handler cpu_x86_signal_handler #define cpu_list x86_cpu_list -#define CPU_SAVE_VERSION 6 +#define CPU_SAVE_VERSION 7 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff -Nur kvm-76/qemu/target-i386/helper.c kvm-userspace/qemu/target-i386/helper.c --- kvm-76/qemu/target-i386/helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -154,9 +154,9 @@ { .name = "qemu64", .level = 2, - .vendor1 = 0x68747541, /* "Auth" */ - .vendor2 = 0x69746e65, /* "enti" */ - .vendor3 = 0x444d4163, /* "cAMD" */ + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, .family = 6, .model = 2, .stepping = 3, @@ -173,6 +173,27 @@ .xlevel = 0x8000000A, .model_id = "QEMU Virtual CPU version " QEMU_VERSION, }, + { + .name = "core2duo", + .level = 10, + .family = 6, + .model = 15, + .stepping = 11, + /* The original CPU also implements these features: + CPUID_VME, CPUID_DTS, CPUID_ACPI, CPUID_SS, CPUID_HT, + CPUID_TM, CPUID_PBE */ + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | + CPUID_PSE36, + /* The original CPU also implements these ext features: + CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, + CPUID_EXT_TM2, CPUID_EXT_CX16, CPUID_EXT_XTPR, CPUID_EXT_PDCM */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3, + .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + /* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */ + .xlevel = 0x8000000A, + .model_id = "Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz", + }, #endif { .name = "qemu32", @@ -230,12 +251,33 @@ .family = 6, .model = 2, .stepping = 3, - .features = PPRO_FEATURES | PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, + .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, .xlevel = 0x80000008, /* XXX: put another string ? */ .model_id = "QEMU Virtual CPU version " QEMU_VERSION, }, + { + .name = "n270", + /* original is on level 10 */ + .level = 5, + .family = 6, + .model = 28, + .stepping = 2, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME, + /* Missing: CPUID_DTS | CPUID_ACPI | CPUID_SS | + * CPUID_HT | CPUID_TM | CPUID_PBE */ + /* Some CPUs got no CPUID_SEP */ + .ext_features = CPUID_EXT_MONITOR | + CPUID_EXT_SSE3 /* PNI */, CPUID_EXT_SSSE3, + /* Missing: CPUID_EXT_DSCPL | CPUID_EXT_EST | + * CPUID_EXT_TM2 | CPUID_EXT_XTPR */ + .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_NX, + /* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */ + .xlevel = 0x8000000A, + .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", + }, }; static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) @@ -355,9 +397,9 @@ env->cpuid_vendor2 = def->vendor2; env->cpuid_vendor3 = def->vendor3; } else { - env->cpuid_vendor1 = 0x756e6547; /* "Genu" */ - env->cpuid_vendor2 = 0x49656e69; /* "ineI" */ - env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */ + env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1; + env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2; + env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3; } env->cpuid_level = def->level; env->cpuid_version = (def->family << 8) | (def->model << 4) | def->stepping; diff -Nur kvm-76/qemu/target-i386/helper.h kvm-userspace/qemu/target-i386/helper.h --- kvm-76/qemu/target-i386/helper.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/helper.h 2008-10-12 09:38:24.000000000 +0200 @@ -55,7 +55,7 @@ DEF_HELPER(void, helper_enter64_level, (int level, int data64, target_ulong t1)) #endif DEF_HELPER(void, helper_sysenter, (void)) -DEF_HELPER(void, helper_sysexit, (void)) +DEF_HELPER(void, helper_sysexit, (int dflag)) #ifdef TARGET_X86_64 DEF_HELPER(void, helper_syscall, (int next_eip_addend)) DEF_HELPER(void, helper_sysret, (int dflag)) diff -Nur kvm-76/qemu/target-i386/machine.c kvm-userspace/qemu/target-i386/machine.c --- kvm-76/qemu/target-i386/machine.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/machine.c 2008-10-12 09:38:24.000000000 +0200 @@ -95,8 +95,8 @@ cpu_put_seg(f, &env->idt); qemu_put_be32s(f, &env->sysenter_cs); - qemu_put_be32s(f, &env->sysenter_esp); - qemu_put_be32s(f, &env->sysenter_eip); + qemu_put_betls(f, &env->sysenter_esp); + qemu_put_betls(f, &env->sysenter_eip); qemu_put_betls(f, &env->cr[0]); qemu_put_betls(f, &env->cr[2]); @@ -108,7 +108,7 @@ /* MMU */ a20_mask = (int32_t) env->a20_mask; - qemu_put_be32s(f, &a20_mask); + qemu_put_sbe32s(f, &a20_mask); /* XMM */ qemu_put_be32s(f, &env->mxcsr); @@ -182,7 +182,7 @@ int32_t a20_mask; if (version_id != 3 && version_id != 4 && version_id != 5 - && version_id != 6) + && version_id != 6 && version_id != 7) return -EINVAL; for(i = 0; i < CPU_NB_REGS; i++) qemu_get_betls(f, &env->regs[i]); @@ -257,8 +257,13 @@ cpu_get_seg(f, &env->idt); qemu_get_be32s(f, &env->sysenter_cs); - qemu_get_be32s(f, &env->sysenter_esp); - qemu_get_be32s(f, &env->sysenter_eip); + if (version_id >= 7) { + qemu_get_betls(f, &env->sysenter_esp); + qemu_get_betls(f, &env->sysenter_eip); + } else { + qemu_get_be32s(f, &env->sysenter_esp); + qemu_get_be32s(f, &env->sysenter_eip); + } qemu_get_betls(f, &env->cr[0]); qemu_get_betls(f, &env->cr[2]); @@ -269,7 +274,7 @@ qemu_get_betls(f, &env->dr[i]); /* MMU */ - qemu_get_be32s(f, &a20_mask); + qemu_get_sbe32s(f, &a20_mask); env->a20_mask = a20_mask; qemu_get_be32s(f, &env->mxcsr); diff -Nur kvm-76/qemu/target-i386/op_helper.c kvm-userspace/qemu/target-i386/op_helper.c --- kvm-76/qemu/target-i386/op_helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/op_helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -1919,6 +1919,64 @@ ECX = 0; EDX = 0x2c307d; break; + case 4: + /* cache info: needed for Core compatibility */ + switch (ECX) { + case 0: /* L1 dcache info */ + EAX = 0x0000121; + EBX = 0x1c0003f; + ECX = 0x000003f; + EDX = 0x0000001; + break; + case 1: /* L1 icache info */ + EAX = 0x0000122; + EBX = 0x1c0003f; + ECX = 0x000003f; + EDX = 0x0000001; + break; + case 2: /* L2 cache info */ + EAX = 0x0000143; + EBX = 0x3c0003f; + ECX = 0x0000fff; + EDX = 0x0000001; + break; + default: /* end of info */ + EAX = 0; + EBX = 0; + ECX = 0; + EDX = 0; + break; + } + + break; + case 5: + /* mwait info: needed for Core compatibility */ + EAX = 0; /* Smallest monitor-line size in bytes */ + EBX = 0; /* Largest monitor-line size in bytes */ + ECX = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + EDX = 0; + break; + case 6: + /* Thermal and Power Leaf */ + EAX = 0; + EBX = 0; + ECX = 0; + EDX = 0; + break; + case 9: + /* Direct Cache Access Information Leaf */ + EAX = 0; /* Bits 0-31 in DCA_CAP MSR */ + EBX = 0; + ECX = 0; + EDX = 0; + break; + case 0xA: + /* Architectural Performance Monitoring Leaf */ + EAX = 0; + EBX = 0; + ECX = 0; + EDX = 0; + break; case 0x80000000: EAX = env->cpuid_xlevel; EBX = env->cpuid_vendor1; @@ -2593,7 +2651,8 @@ POPW(ssp, sp, sp_mask, new_eflags); } ESP = (ESP & ~sp_mask) | (sp & sp_mask); - load_seg_vm(R_CS, new_cs); + env->segs[R_CS].selector = new_cs; + env->segs[R_CS].base = (new_cs << 4); env->eip = new_eip; if (env->eflags & VM_MASK) eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK; @@ -2882,11 +2941,23 @@ } env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); cpu_x86_set_cpl(env, 0); - cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); + } else +#endif + { + cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + } cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | @@ -2896,7 +2967,7 @@ EIP = env->sysenter_eip; } -void helper_sysexit(void) +void helper_sysexit(int dflag) { int cpl; @@ -2905,16 +2976,32 @@ raise_exception_err(EXCP0D_GPF, 0); } cpu_x86_set_cpl(env, 3); - cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); +#ifdef TARGET_X86_64 + if (dflag == 2) { + cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); + cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } else +#endif + { + cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } ESP = ECX; EIP = EDX; #ifdef USE_KQEMU @@ -3146,6 +3233,12 @@ case MSR_VM_HSAVE_PA: val = env->vm_hsave; break; + case MSR_IA32_PERF_STATUS: + /* tsc_increment_by_tick */ + val = 1000ULL; + /* CPU multiplier */ + val |= (((uint64_t)4ULL) << 40); + break; #ifdef TARGET_X86_64 case MSR_LSTAR: val = env->lstar; diff -Nur kvm-76/qemu/target-i386/ops_sse.h kvm-userspace/qemu/target-i386/ops_sse.h --- kvm-76/qemu/target-i386/ops_sse.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/ops_sse.h 2008-10-12 09:38:24.000000000 +0200 @@ -1,7 +1,8 @@ /* - * MMX/3DNow!/SSE/SSE2/SSE3/PNI support + * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support * * Copyright (c) 2005 Fabrice Bellard + * Copyright (c) 2008 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1275,6 +1276,766 @@ } #endif +/* SSSE3 op helpers */ +void glue(helper_pshufb, SUFFIX) (Reg *d, Reg *s) +{ + int i; + Reg r; + + for (i = 0; i < (8 << SHIFT); i++) + r.B(i) = (s->B(i) & 0x80) ? 0 : (d->B(s->B(i) & ((8 << SHIFT) - 1))); + + *d = r; +} + +void glue(helper_phaddw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = (int16_t)d->W(0) + (int16_t)d->W(1); + d->W(1) = (int16_t)d->W(2) + (int16_t)d->W(3); + XMM_ONLY(d->W(2) = (int16_t)d->W(4) + (int16_t)d->W(5)); + XMM_ONLY(d->W(3) = (int16_t)d->W(6) + (int16_t)d->W(7)); + d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) + (int16_t)s->W(1); + d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) + (int16_t)s->W(3); + XMM_ONLY(d->W(6) = (int16_t)s->W(4) + (int16_t)s->W(5)); + XMM_ONLY(d->W(7) = (int16_t)s->W(6) + (int16_t)s->W(7)); +} + +void glue(helper_phaddd, SUFFIX) (Reg *d, Reg *s) +{ + d->L(0) = (int32_t)d->L(0) + (int32_t)d->L(1); + XMM_ONLY(d->L(1) = (int32_t)d->L(2) + (int32_t)d->L(3)); + d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) + (int32_t)s->L(1); + XMM_ONLY(d->L(3) = (int32_t)s->L(2) + (int32_t)s->L(3)); +} + +void glue(helper_phaddsw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = satsw((int16_t)d->W(0) + (int16_t)d->W(1)); + d->W(1) = satsw((int16_t)d->W(2) + (int16_t)d->W(3)); + XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) + (int16_t)d->W(5))); + XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) + (int16_t)d->W(7))); + d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) + (int16_t)s->W(1)); + d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) + (int16_t)s->W(3)); + XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) + (int16_t)s->W(5))); + XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) + (int16_t)s->W(7))); +} + +void glue(helper_pmaddubsw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = satsw((int8_t)s->B( 0) * (uint8_t)d->B( 0) + + (int8_t)s->B( 1) * (uint8_t)d->B( 1)); + d->W(1) = satsw((int8_t)s->B( 2) * (uint8_t)d->B( 2) + + (int8_t)s->B( 3) * (uint8_t)d->B( 3)); + d->W(2) = satsw((int8_t)s->B( 4) * (uint8_t)d->B( 4) + + (int8_t)s->B( 5) * (uint8_t)d->B( 5)); + d->W(3) = satsw((int8_t)s->B( 6) * (uint8_t)d->B( 6) + + (int8_t)s->B( 7) * (uint8_t)d->B( 7)); +#if SHIFT == 1 + d->W(4) = satsw((int8_t)s->B( 8) * (uint8_t)d->B( 8) + + (int8_t)s->B( 9) * (uint8_t)d->B( 9)); + d->W(5) = satsw((int8_t)s->B(10) * (uint8_t)d->B(10) + + (int8_t)s->B(11) * (uint8_t)d->B(11)); + d->W(6) = satsw((int8_t)s->B(12) * (uint8_t)d->B(12) + + (int8_t)s->B(13) * (uint8_t)d->B(13)); + d->W(7) = satsw((int8_t)s->B(14) * (uint8_t)d->B(14) + + (int8_t)s->B(15) * (uint8_t)d->B(15)); +#endif +} + +void glue(helper_phsubw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = (int16_t)d->W(0) - (int16_t)d->W(1); + d->W(1) = (int16_t)d->W(2) - (int16_t)d->W(3); + XMM_ONLY(d->W(2) = (int16_t)d->W(4) - (int16_t)d->W(5)); + XMM_ONLY(d->W(3) = (int16_t)d->W(6) - (int16_t)d->W(7)); + d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) - (int16_t)s->W(1); + d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) - (int16_t)s->W(3); + XMM_ONLY(d->W(6) = (int16_t)s->W(4) - (int16_t)s->W(5)); + XMM_ONLY(d->W(7) = (int16_t)s->W(6) - (int16_t)s->W(7)); +} + +void glue(helper_phsubd, SUFFIX) (Reg *d, Reg *s) +{ + d->L(0) = (int32_t)d->L(0) - (int32_t)d->L(1); + XMM_ONLY(d->L(1) = (int32_t)d->L(2) - (int32_t)d->L(3)); + d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) - (int32_t)s->L(1); + XMM_ONLY(d->L(3) = (int32_t)s->L(2) - (int32_t)s->L(3)); +} + +void glue(helper_phsubsw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = satsw((int16_t)d->W(0) - (int16_t)d->W(1)); + d->W(1) = satsw((int16_t)d->W(2) - (int16_t)d->W(3)); + XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) - (int16_t)d->W(5))); + XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) - (int16_t)d->W(7))); + d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) - (int16_t)s->W(1)); + d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) - (int16_t)s->W(3)); + XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) - (int16_t)s->W(5))); + XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) - (int16_t)s->W(7))); +} + +#define FABSB(_, x) x > INT8_MAX ? -(int8_t ) x : x +#define FABSW(_, x) x > INT16_MAX ? -(int16_t) x : x +#define FABSL(_, x) x > INT32_MAX ? -(int32_t) x : x +SSE_HELPER_B(helper_pabsb, FABSB) +SSE_HELPER_W(helper_pabsw, FABSW) +SSE_HELPER_L(helper_pabsd, FABSL) + +#define FMULHRSW(d, s) ((int16_t) d * (int16_t) s + 0x4000) >> 15 +SSE_HELPER_W(helper_pmulhrsw, FMULHRSW) + +#define FSIGNB(d, s) s <= INT8_MAX ? s ? d : 0 : -(int8_t ) d +#define FSIGNW(d, s) s <= INT16_MAX ? s ? d : 0 : -(int16_t) d +#define FSIGNL(d, s) s <= INT32_MAX ? s ? d : 0 : -(int32_t) d +SSE_HELPER_B(helper_psignb, FSIGNB) +SSE_HELPER_W(helper_psignw, FSIGNW) +SSE_HELPER_L(helper_psignd, FSIGNL) + +void glue(helper_palignr, SUFFIX) (Reg *d, Reg *s, int32_t shift) +{ + Reg r; + + /* XXX could be checked during translation */ + if (shift >= (16 << SHIFT)) { + r.Q(0) = 0; + XMM_ONLY(r.Q(1) = 0); + } else { + shift <<= 3; +#define SHR(v, i) (i < 64 && i > -64 ? i > 0 ? v >> (i) : (v << -(i)) : 0) +#if SHIFT == 0 + r.Q(0) = SHR(s->Q(0), shift - 0) | + SHR(d->Q(0), shift - 64); +#else + r.Q(0) = SHR(s->Q(0), shift - 0) | + SHR(s->Q(1), shift - 64) | + SHR(d->Q(0), shift - 128) | + SHR(d->Q(1), shift - 192); + r.Q(1) = SHR(s->Q(0), shift + 64) | + SHR(s->Q(1), shift - 0) | + SHR(d->Q(0), shift - 64) | + SHR(d->Q(1), shift - 128); +#endif +#undef SHR + } + + *d = r; +} + +#define XMM0 env->xmm_regs[0] + +#if SHIFT == 1 +#define SSE_HELPER_V(name, elem, num, F)\ +void glue(name, SUFFIX) (Reg *d, Reg *s)\ +{\ + d->elem(0) = F(d->elem(0), s->elem(0), XMM0.elem(0));\ + d->elem(1) = F(d->elem(1), s->elem(1), XMM0.elem(1));\ + if (num > 2) {\ + d->elem(2) = F(d->elem(2), s->elem(2), XMM0.elem(2));\ + d->elem(3) = F(d->elem(3), s->elem(3), XMM0.elem(3));\ + if (num > 4) {\ + d->elem(4) = F(d->elem(4), s->elem(4), XMM0.elem(4));\ + d->elem(5) = F(d->elem(5), s->elem(5), XMM0.elem(5));\ + d->elem(6) = F(d->elem(6), s->elem(6), XMM0.elem(6));\ + d->elem(7) = F(d->elem(7), s->elem(7), XMM0.elem(7));\ + if (num > 8) {\ + d->elem(8) = F(d->elem(8), s->elem(8), XMM0.elem(8));\ + d->elem(9) = F(d->elem(9), s->elem(9), XMM0.elem(9));\ + d->elem(10) = F(d->elem(10), s->elem(10), XMM0.elem(10));\ + d->elem(11) = F(d->elem(11), s->elem(11), XMM0.elem(11));\ + d->elem(12) = F(d->elem(12), s->elem(12), XMM0.elem(12));\ + d->elem(13) = F(d->elem(13), s->elem(13), XMM0.elem(13));\ + d->elem(14) = F(d->elem(14), s->elem(14), XMM0.elem(14));\ + d->elem(15) = F(d->elem(15), s->elem(15), XMM0.elem(15));\ + }\ + }\ + }\ +} + +#define SSE_HELPER_I(name, elem, num, F)\ +void glue(name, SUFFIX) (Reg *d, Reg *s, uint32_t imm)\ +{\ + d->elem(0) = F(d->elem(0), s->elem(0), ((imm >> 0) & 1));\ + d->elem(1) = F(d->elem(1), s->elem(1), ((imm >> 1) & 1));\ + if (num > 2) {\ + d->elem(2) = F(d->elem(2), s->elem(2), ((imm >> 2) & 1));\ + d->elem(3) = F(d->elem(3), s->elem(3), ((imm >> 3) & 1));\ + if (num > 4) {\ + d->elem(4) = F(d->elem(4), s->elem(4), ((imm >> 4) & 1));\ + d->elem(5) = F(d->elem(5), s->elem(5), ((imm >> 5) & 1));\ + d->elem(6) = F(d->elem(6), s->elem(6), ((imm >> 6) & 1));\ + d->elem(7) = F(d->elem(7), s->elem(7), ((imm >> 7) & 1));\ + if (num > 8) {\ + d->elem(8) = F(d->elem(8), s->elem(8), ((imm >> 8) & 1));\ + d->elem(9) = F(d->elem(9), s->elem(9), ((imm >> 9) & 1));\ + d->elem(10) = F(d->elem(10), s->elem(10), ((imm >> 10) & 1));\ + d->elem(11) = F(d->elem(11), s->elem(11), ((imm >> 11) & 1));\ + d->elem(12) = F(d->elem(12), s->elem(12), ((imm >> 12) & 1));\ + d->elem(13) = F(d->elem(13), s->elem(13), ((imm >> 13) & 1));\ + d->elem(14) = F(d->elem(14), s->elem(14), ((imm >> 14) & 1));\ + d->elem(15) = F(d->elem(15), s->elem(15), ((imm >> 15) & 1));\ + }\ + }\ + }\ +} + +/* SSE4.1 op helpers */ +#define FBLENDVB(d, s, m) (m & 0x80) ? s : d +#define FBLENDVPS(d, s, m) (m & 0x80000000) ? s : d +#define FBLENDVPD(d, s, m) (m & 0x8000000000000000LL) ? s : d +SSE_HELPER_V(helper_pblendvb, B, 16, FBLENDVB) +SSE_HELPER_V(helper_blendvps, L, 4, FBLENDVPS) +SSE_HELPER_V(helper_blendvpd, Q, 2, FBLENDVPD) + +void glue(helper_ptest, SUFFIX) (Reg *d, Reg *s) +{ + uint64_t zf = (s->Q(0) & d->Q(0)) | (s->Q(1) & d->Q(1)); + uint64_t cf = (s->Q(0) & ~d->Q(0)) | (s->Q(1) & ~d->Q(1)); + + CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C); +} + +#define SSE_HELPER_F(name, elem, num, F)\ +void glue(name, SUFFIX) (Reg *d, Reg *s)\ +{\ + d->elem(0) = F(0);\ + d->elem(1) = F(1);\ + d->elem(2) = F(2);\ + d->elem(3) = F(3);\ + if (num > 3) {\ + d->elem(4) = F(4);\ + d->elem(5) = F(5);\ + if (num > 5) {\ + d->elem(6) = F(6);\ + d->elem(7) = F(7);\ + }\ + }\ +} + +SSE_HELPER_F(helper_pmovsxbw, W, 8, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbd, L, 4, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbq, Q, 2, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxwd, L, 4, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxwq, Q, 2, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxdq, Q, 2, (int32_t) s->L) +SSE_HELPER_F(helper_pmovzxbw, W, 8, s->B) +SSE_HELPER_F(helper_pmovzxbd, L, 4, s->B) +SSE_HELPER_F(helper_pmovzxbq, Q, 2, s->B) +SSE_HELPER_F(helper_pmovzxwd, L, 4, s->W) +SSE_HELPER_F(helper_pmovzxwq, Q, 2, s->W) +SSE_HELPER_F(helper_pmovzxdq, Q, 2, s->L) + +void glue(helper_pmuldq, SUFFIX) (Reg *d, Reg *s) +{ + d->Q(0) = (int64_t) (int32_t) d->L(0) * (int32_t) s->L(0); + d->Q(1) = (int64_t) (int32_t) d->L(2) * (int32_t) s->L(2); +} + +#define FCMPEQQ(d, s) d == s ? -1 : 0 +SSE_HELPER_Q(helper_pcmpeqq, FCMPEQQ) + +void glue(helper_packusdw, SUFFIX) (Reg *d, Reg *s) +{ + d->W(0) = satuw((int32_t) d->L(0)); + d->W(1) = satuw((int32_t) d->L(1)); + d->W(2) = satuw((int32_t) d->L(2)); + d->W(3) = satuw((int32_t) d->L(3)); + d->W(4) = satuw((int32_t) s->L(0)); + d->W(5) = satuw((int32_t) s->L(1)); + d->W(6) = satuw((int32_t) s->L(2)); + d->W(7) = satuw((int32_t) s->L(3)); +} + +#define FMINSB(d, s) MIN((int8_t) d, (int8_t) s) +#define FMINSD(d, s) MIN((int32_t) d, (int32_t) s) +#define FMAXSB(d, s) MAX((int8_t) d, (int8_t) s) +#define FMAXSD(d, s) MAX((int32_t) d, (int32_t) s) +SSE_HELPER_B(helper_pminsb, FMINSB) +SSE_HELPER_L(helper_pminsd, FMINSD) +SSE_HELPER_W(helper_pminuw, MIN) +SSE_HELPER_L(helper_pminud, MIN) +SSE_HELPER_B(helper_pmaxsb, FMAXSB) +SSE_HELPER_L(helper_pmaxsd, FMAXSD) +SSE_HELPER_W(helper_pmaxuw, MAX) +SSE_HELPER_L(helper_pmaxud, MAX) + +#define FMULLD(d, s) (int32_t) d * (int32_t) s +SSE_HELPER_L(helper_pmulld, FMULLD) + +void glue(helper_phminposuw, SUFFIX) (Reg *d, Reg *s) +{ + int idx = 0; + + if (s->W(1) < s->W(idx)) + idx = 1; + if (s->W(2) < s->W(idx)) + idx = 2; + if (s->W(3) < s->W(idx)) + idx = 3; + if (s->W(4) < s->W(idx)) + idx = 4; + if (s->W(5) < s->W(idx)) + idx = 5; + if (s->W(6) < s->W(idx)) + idx = 6; + if (s->W(7) < s->W(idx)) + idx = 7; + + d->Q(1) = 0; + d->L(1) = 0; + d->W(1) = idx; + d->W(0) = s->W(idx); +} + +void glue(helper_roundps, SUFFIX) (Reg *d, Reg *s, uint32_t mode) +{ + signed char prev_rounding_mode; + + prev_rounding_mode = env->sse_status.float_rounding_mode; + if (!(mode & (1 << 2))) + switch (mode & 3) { + case 0: + set_float_rounding_mode(float_round_nearest_even, &env->sse_status); + break; + case 1: + set_float_rounding_mode(float_round_down, &env->sse_status); + break; + case 2: + set_float_rounding_mode(float_round_up, &env->sse_status); + break; + case 3: + set_float_rounding_mode(float_round_to_zero, &env->sse_status); + break; + } + + d->L(0) = float64_round_to_int(s->L(0), &env->sse_status); + d->L(1) = float64_round_to_int(s->L(1), &env->sse_status); + d->L(2) = float64_round_to_int(s->L(2), &env->sse_status); + d->L(3) = float64_round_to_int(s->L(3), &env->sse_status); + +#if 0 /* TODO */ + if (mode & (1 << 3)) + set_float_exception_flags( + get_float_exception_flags(&env->sse_status) & + ~float_flag_inexact, + &env->sse_status); +#endif + env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundpd, SUFFIX) (Reg *d, Reg *s, uint32_t mode) +{ + signed char prev_rounding_mode; + + prev_rounding_mode = env->sse_status.float_rounding_mode; + if (!(mode & (1 << 2))) + switch (mode & 3) { + case 0: + set_float_rounding_mode(float_round_nearest_even, &env->sse_status); + break; + case 1: + set_float_rounding_mode(float_round_down, &env->sse_status); + break; + case 2: + set_float_rounding_mode(float_round_up, &env->sse_status); + break; + case 3: + set_float_rounding_mode(float_round_to_zero, &env->sse_status); + break; + } + + d->Q(0) = float64_round_to_int(s->Q(0), &env->sse_status); + d->Q(1) = float64_round_to_int(s->Q(1), &env->sse_status); + +#if 0 /* TODO */ + if (mode & (1 << 3)) + set_float_exception_flags( + get_float_exception_flags(&env->sse_status) & + ~float_flag_inexact, + &env->sse_status); +#endif + env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundss, SUFFIX) (Reg *d, Reg *s, uint32_t mode) +{ + signed char prev_rounding_mode; + + prev_rounding_mode = env->sse_status.float_rounding_mode; + if (!(mode & (1 << 2))) + switch (mode & 3) { + case 0: + set_float_rounding_mode(float_round_nearest_even, &env->sse_status); + break; + case 1: + set_float_rounding_mode(float_round_down, &env->sse_status); + break; + case 2: + set_float_rounding_mode(float_round_up, &env->sse_status); + break; + case 3: + set_float_rounding_mode(float_round_to_zero, &env->sse_status); + break; + } + + d->L(0) = float64_round_to_int(s->L(0), &env->sse_status); + +#if 0 /* TODO */ + if (mode & (1 << 3)) + set_float_exception_flags( + get_float_exception_flags(&env->sse_status) & + ~float_flag_inexact, + &env->sse_status); +#endif + env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundsd, SUFFIX) (Reg *d, Reg *s, uint32_t mode) +{ + signed char prev_rounding_mode; + + prev_rounding_mode = env->sse_status.float_rounding_mode; + if (!(mode & (1 << 2))) + switch (mode & 3) { + case 0: + set_float_rounding_mode(float_round_nearest_even, &env->sse_status); + break; + case 1: + set_float_rounding_mode(float_round_down, &env->sse_status); + break; + case 2: + set_float_rounding_mode(float_round_up, &env->sse_status); + break; + case 3: + set_float_rounding_mode(float_round_to_zero, &env->sse_status); + break; + } + + d->Q(0) = float64_round_to_int(s->Q(0), &env->sse_status); + +#if 0 /* TODO */ + if (mode & (1 << 3)) + set_float_exception_flags( + get_float_exception_flags(&env->sse_status) & + ~float_flag_inexact, + &env->sse_status); +#endif + env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +#define FBLENDP(d, s, m) m ? s : d +SSE_HELPER_I(helper_blendps, L, 4, FBLENDP) +SSE_HELPER_I(helper_blendpd, Q, 2, FBLENDP) +SSE_HELPER_I(helper_pblendw, W, 8, FBLENDP) + +void glue(helper_dpps, SUFFIX) (Reg *d, Reg *s, uint32_t mask) +{ + float32 iresult = 0 /*float32_zero*/; + + if (mask & (1 << 4)) + iresult = float32_add(iresult, + float32_mul(d->L(0), s->L(0), &env->sse_status), + &env->sse_status); + if (mask & (1 << 5)) + iresult = float32_add(iresult, + float32_mul(d->L(1), s->L(1), &env->sse_status), + &env->sse_status); + if (mask & (1 << 6)) + iresult = float32_add(iresult, + float32_mul(d->L(2), s->L(2), &env->sse_status), + &env->sse_status); + if (mask & (1 << 7)) + iresult = float32_add(iresult, + float32_mul(d->L(3), s->L(3), &env->sse_status), + &env->sse_status); + d->L(0) = (mask & (1 << 0)) ? iresult : 0 /*float32_zero*/; + d->L(1) = (mask & (1 << 1)) ? iresult : 0 /*float32_zero*/; + d->L(2) = (mask & (1 << 2)) ? iresult : 0 /*float32_zero*/; + d->L(3) = (mask & (1 << 3)) ? iresult : 0 /*float32_zero*/; +} + +void glue(helper_dppd, SUFFIX) (Reg *d, Reg *s, uint32_t mask) +{ + float64 iresult = 0 /*float64_zero*/; + + if (mask & (1 << 4)) + iresult = float64_add(iresult, + float64_mul(d->Q(0), s->Q(0), &env->sse_status), + &env->sse_status); + if (mask & (1 << 5)) + iresult = float64_add(iresult, + float64_mul(d->Q(1), s->Q(1), &env->sse_status), + &env->sse_status); + d->Q(0) = (mask & (1 << 0)) ? iresult : 0 /*float64_zero*/; + d->Q(1) = (mask & (1 << 1)) ? iresult : 0 /*float64_zero*/; +} + +void glue(helper_mpsadbw, SUFFIX) (Reg *d, Reg *s, uint32_t offset) +{ + int s0 = (offset & 3) << 2; + int d0 = (offset & 4) << 0; + int i; + Reg r; + + for (i = 0; i < 8; i++, d0++) { + r.W(i) = 0; + r.W(i) += abs1(d->B(d0 + 0) - s->B(s0 + 0)); + r.W(i) += abs1(d->B(d0 + 1) - s->B(s0 + 1)); + r.W(i) += abs1(d->B(d0 + 2) - s->B(s0 + 2)); + r.W(i) += abs1(d->B(d0 + 3) - s->B(s0 + 3)); + } + + *d = r; +} + +/* SSE4.2 op helpers */ +/* it's unclear whether signed or unsigned */ +#define FCMPGTQ(d, s) d > s ? -1 : 0 +SSE_HELPER_Q(helper_pcmpgtq, FCMPGTQ) + +static inline int pcmp_elen(int reg, uint32_t ctrl) +{ + int val; + + /* Presence of REX.W is indicated by a bit higher than 7 set */ + if (ctrl >> 8) + val = abs1((int64_t) env->regs[reg]); + else + val = abs1((int32_t) env->regs[reg]); + + if (ctrl & 1) { + if (val > 8) + return 8; + } else + if (val > 16) + return 16; + + return val; +} + +static inline int pcmp_ilen(Reg *r, uint8_t ctrl) +{ + int val = 0; + + if (ctrl & 1) { + while (val < 8 && r->W(val)) + val++; + } else + while (val < 16 && r->B(val)) + val++; + + return val; +} + +static inline int pcmp_val(Reg *r, uint8_t ctrl, int i) +{ + switch ((ctrl >> 0) & 3) { + case 0: + return r->B(i); + case 1: + return r->W(i); + case 2: + return (int8_t) r->B(i); + case 3: + default: + return (int16_t) r->W(i); + } +} + +static inline unsigned pcmpxstrx(Reg *d, Reg *s, + int8_t ctrl, int valids, int validd) +{ + unsigned int res = 0; + int v; + int j, i; + int upper = (ctrl & 1) ? 7 : 15; + + valids--; + validd--; + + CC_SRC = (valids < upper ? CC_Z : 0) | (validd < upper ? CC_S : 0); + + switch ((ctrl >> 2) & 3) { + case 0: + for (j = valids; j >= 0; j--) { + res <<= 1; + v = pcmp_val(s, ctrl, j); + for (i = validd; i >= 0; i--) + res |= (v == pcmp_val(d, ctrl, i)); + } + break; + case 1: + for (j = valids; j >= 0; j--) { + res <<= 1; + v = pcmp_val(s, ctrl, j); + for (i = ((validd - 1) | 1); i >= 0; i -= 2) + res |= (pcmp_val(d, ctrl, i - 0) <= v && + pcmp_val(d, ctrl, i - 1) >= v); + } + break; + case 2: + res = (2 << (upper - MAX(valids, validd))) - 1; + res <<= MAX(valids, validd) - MIN(valids, validd); + for (i = MIN(valids, validd); i >= 0; i--) { + res <<= 1; + v = pcmp_val(s, ctrl, i); + res |= (v == pcmp_val(d, ctrl, i)); + } + break; + case 3: + for (j = valids - validd; j >= 0; j--) { + res <<= 1; + res |= 1; + for (i = MIN(upper - j, validd); i >= 0; i--) + res &= (pcmp_val(s, ctrl, i + j) == pcmp_val(d, ctrl, i)); + } + break; + } + + switch ((ctrl >> 4) & 3) { + case 1: + res ^= (2 << upper) - 1; + break; + case 3: + res ^= (2 << valids) - 1; + break; + } + + if (res) + CC_SRC |= CC_C; + if (res & 1) + CC_SRC |= CC_O; + + return res; +} + +static inline int rffs1(unsigned int val) +{ + int ret = 1, hi; + + for (hi = sizeof(val) * 4; hi; hi /= 2) + if (val >> hi) { + val >>= hi; + ret += hi; + } + + return ret; +} + +static inline int ffs1(unsigned int val) +{ + int ret = 1, hi; + + for (hi = sizeof(val) * 4; hi; hi /= 2) + if (val << hi) { + val <<= hi; + ret += hi; + } + + return ret; +} + +void glue(helper_pcmpestri, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl) +{ + unsigned int res = pcmpxstrx(d, s, ctrl, + pcmp_elen(R_EDX, ctrl), + pcmp_elen(R_EAX, ctrl)); + + if (res) + env->regs[R_ECX] = ((ctrl & (1 << 6)) ? rffs1 : ffs1)(res) - 1; + else + env->regs[R_ECX] = 16 >> (ctrl & (1 << 0)); +} + +void glue(helper_pcmpestrm, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl) +{ + int i; + unsigned int res = pcmpxstrx(d, s, ctrl, + pcmp_elen(R_EDX, ctrl), + pcmp_elen(R_EAX, ctrl)); + + if ((ctrl >> 6) & 1) { + if (ctrl & 1) + for (i = 0; i <= 8; i--, res >>= 1) + d->W(i) = (res & 1) ? ~0 : 0; + else + for (i = 0; i <= 16; i--, res >>= 1) + d->B(i) = (res & 1) ? ~0 : 0; + } else { + d->Q(1) = 0; + d->Q(0) = res; + } +} + +void glue(helper_pcmpistri, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl) +{ + unsigned int res = pcmpxstrx(d, s, ctrl, + pcmp_ilen(s, ctrl), + pcmp_ilen(d, ctrl)); + + if (res) + env->regs[R_ECX] = ((ctrl & (1 << 6)) ? rffs1 : ffs1)(res) - 1; + else + env->regs[R_ECX] = 16 >> (ctrl & (1 << 0)); +} + +void glue(helper_pcmpistrm, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl) +{ + int i; + unsigned int res = pcmpxstrx(d, s, ctrl, + pcmp_ilen(s, ctrl), + pcmp_ilen(d, ctrl)); + + if ((ctrl >> 6) & 1) { + if (ctrl & 1) + for (i = 0; i <= 8; i--, res >>= 1) + d->W(i) = (res & 1) ? ~0 : 0; + else + for (i = 0; i <= 16; i--, res >>= 1) + d->B(i) = (res & 1) ? ~0 : 0; + } else { + d->Q(1) = 0; + d->Q(0) = res; + } +} + +#define CRCPOLY 0x1edc6f41 +#define CRCPOLY_BITREV 0x82f63b78 +target_ulong helper_crc32(uint32_t crc1, target_ulong msg, uint32_t len) +{ + target_ulong crc = (msg & ((target_ulong) -1 >> + (TARGET_LONG_BITS - len))) ^ crc1; + + while (len--) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_BITREV : 0); + + return crc; +} + +#define POPMASK(i) ((target_ulong) -1 / ((1LL << (1 << i)) + 1)) +#define POPCOUNT(n, i) (n & POPMASK(i)) + ((n >> (1 << i)) & POPMASK(i)) +target_ulong helper_popcnt(target_ulong n, uint32_t type) +{ + CC_SRC = n ? 0 : CC_Z; + + n = POPCOUNT(n, 0); + n = POPCOUNT(n, 1); + n = POPCOUNT(n, 2); + n = POPCOUNT(n, 3); + if (type == 1) + return n & 0xff; + + n = POPCOUNT(n, 4); +#ifndef TARGET_X86_64 + return n; +#else + if (type == 2) + return n & 0xff; + + return POPCOUNT(n, 5); +#endif +} +#endif + #undef SHIFT #undef XMM_ONLY #undef Reg diff -Nur kvm-76/qemu/target-i386/ops_sse_header.h kvm-userspace/qemu/target-i386/ops_sse_header.h --- kvm-76/qemu/target-i386/ops_sse_header.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/ops_sse_header.h 2008-10-12 09:38:24.000000000 +0200 @@ -1,5 +1,5 @@ /* - * MMX/3DNow!/SSE/SSE2/SSE3/PNI support + * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support * * Copyright (c) 2005 Fabrice Bellard * @@ -251,6 +251,79 @@ DEF_HELPER(void, helper_pswapd, (MMXReg *d, MMXReg *s)) #endif +/* SSSE3 op helpers */ +DEF_HELPER(void, glue(helper_phaddw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phaddd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phaddsw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phsubw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phsubd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phsubsw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pabsb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pabsw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pabsd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmaddubsw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmulhrsw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pshufb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_psignb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_psignw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_psignd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_palignr, SUFFIX), (Reg *d, Reg *s, int32_t shift)) + +/* SSE4.1 op helpers */ +#if SHIFT == 1 +DEF_HELPER(void, glue(helper_pblendvb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_blendvps, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_blendvpd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_ptest, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxbw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxbd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxbq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxwd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxwq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovsxdq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxbw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxbd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxbq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxwd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxwq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmovzxdq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmuldq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pcmpeqq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_packusdw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pminsb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pminsd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pminuw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pminud, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmaxsb, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmaxsd, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmaxuw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmaxud, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pmulld, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_phminposuw, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_roundps, SUFFIX), (Reg *d, Reg *s, uint32_t mode)) +DEF_HELPER(void, glue(helper_roundpd, SUFFIX), (Reg *d, Reg *s, uint32_t mode)) +DEF_HELPER(void, glue(helper_roundss, SUFFIX), (Reg *d, Reg *s, uint32_t mode)) +DEF_HELPER(void, glue(helper_roundsd, SUFFIX), (Reg *d, Reg *s, uint32_t mode)) +DEF_HELPER(void, glue(helper_blendps, SUFFIX), (Reg *d, Reg *s, uint32_t imm)) +DEF_HELPER(void, glue(helper_blendpd, SUFFIX), (Reg *d, Reg *s, uint32_t imm)) +DEF_HELPER(void, glue(helper_pblendw, SUFFIX), (Reg *d, Reg *s, uint32_t imm)) +DEF_HELPER(void, glue(helper_dpps, SUFFIX), (Reg *d, Reg *s, uint32_t mask)) +DEF_HELPER(void, glue(helper_dppd, SUFFIX), (Reg *d, Reg *s, uint32_t mask)) +DEF_HELPER(void, glue(helper_mpsadbw, SUFFIX), (Reg *d, Reg *s, uint32_t off)) +#endif + +/* SSE4.2 op helpers */ +#if SHIFT == 1 +DEF_HELPER(void, glue(helper_pcmpgtq, SUFFIX), (Reg *d, Reg *s)) +DEF_HELPER(void, glue(helper_pcmpestri, SUFFIX), (Reg *d, Reg *s, uint32_t ctl)) +DEF_HELPER(void, glue(helper_pcmpestrm, SUFFIX), (Reg *d, Reg *s, uint32_t ctl)) +DEF_HELPER(void, glue(helper_pcmpistri, SUFFIX), (Reg *d, Reg *s, uint32_t ctl)) +DEF_HELPER(void, glue(helper_pcmpistrm, SUFFIX), (Reg *d, Reg *s, uint32_t ctl)) +DEF_HELPER(target_ulong, helper_crc32, + (uint32_t crc1, target_ulong msg, uint32_t len)) +DEF_HELPER(target_ulong, helper_popcnt, (target_ulong n, uint32_t type)) +#endif + #undef SHIFT #undef Reg #undef SUFFIX diff -Nur kvm-76/qemu/target-i386/translate.c kvm-userspace/qemu/target-i386/translate.c --- kvm-76/qemu/target-i386/translate.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-i386/translate.c 2008-10-12 09:38:24.000000000 +0200 @@ -2140,7 +2140,7 @@ } } -/* generate modrm memory load or store of 'reg'. TMP0 is used if reg != +/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == OR_TMP0 */ static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store) { @@ -2770,6 +2770,9 @@ [0xc2] = SSE_FOP(cmpeq), [0xc6] = { helper_shufps, helper_shufpd }, + [0x38] = { SSE_SPECIAL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* SSSE3/SSE4 */ + [0x3a] = { SSE_SPECIAL, SSE_SPECIAL }, /* SSSE3/SSE4 */ + /* MMX ops and their SSE extensions */ [0x60] = MMX_OP2(punpcklbw), [0x61] = MMX_OP2(punpcklwd), @@ -2921,6 +2924,87 @@ [0xbf] = helper_pavgb_mmx /* pavgusb */ }; +struct sse_op_helper_s { + void *op[2]; uint32_t ext_mask; +}; +#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 } +#define SSE41_OP(x) { { NULL, helper_ ## x ## _xmm }, CPUID_EXT_SSE41 } +#define SSE42_OP(x) { { NULL, helper_ ## x ## _xmm }, CPUID_EXT_SSE42 } +#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 } +static struct sse_op_helper_s sse_op_table6[256] = { + [0x00] = SSSE3_OP(pshufb), + [0x01] = SSSE3_OP(phaddw), + [0x02] = SSSE3_OP(phaddd), + [0x03] = SSSE3_OP(phaddsw), + [0x04] = SSSE3_OP(pmaddubsw), + [0x05] = SSSE3_OP(phsubw), + [0x06] = SSSE3_OP(phsubd), + [0x07] = SSSE3_OP(phsubsw), + [0x08] = SSSE3_OP(psignb), + [0x09] = SSSE3_OP(psignw), + [0x0a] = SSSE3_OP(psignd), + [0x0b] = SSSE3_OP(pmulhrsw), + [0x10] = SSE41_OP(pblendvb), + [0x14] = SSE41_OP(blendvps), + [0x15] = SSE41_OP(blendvpd), + [0x17] = SSE41_OP(ptest), + [0x1c] = SSSE3_OP(pabsb), + [0x1d] = SSSE3_OP(pabsw), + [0x1e] = SSSE3_OP(pabsd), + [0x20] = SSE41_OP(pmovsxbw), + [0x21] = SSE41_OP(pmovsxbd), + [0x22] = SSE41_OP(pmovsxbq), + [0x23] = SSE41_OP(pmovsxwd), + [0x24] = SSE41_OP(pmovsxwq), + [0x25] = SSE41_OP(pmovsxdq), + [0x28] = SSE41_OP(pmuldq), + [0x29] = SSE41_OP(pcmpeqq), + [0x2a] = SSE41_SPECIAL, /* movntqda */ + [0x2b] = SSE41_OP(packusdw), + [0x30] = SSE41_OP(pmovzxbw), + [0x31] = SSE41_OP(pmovzxbd), + [0x32] = SSE41_OP(pmovzxbq), + [0x33] = SSE41_OP(pmovzxwd), + [0x34] = SSE41_OP(pmovzxwq), + [0x35] = SSE41_OP(pmovzxdq), + [0x37] = SSE42_OP(pcmpgtq), + [0x38] = SSE41_OP(pminsb), + [0x39] = SSE41_OP(pminsd), + [0x3a] = SSE41_OP(pminuw), + [0x3b] = SSE41_OP(pminud), + [0x3c] = SSE41_OP(pmaxsb), + [0x3d] = SSE41_OP(pmaxsd), + [0x3e] = SSE41_OP(pmaxuw), + [0x3f] = SSE41_OP(pmaxud), + [0x40] = SSE41_OP(pmulld), + [0x41] = SSE41_OP(phminposuw), +}; + +static struct sse_op_helper_s sse_op_table7[256] = { + [0x08] = SSE41_OP(roundps), + [0x09] = SSE41_OP(roundpd), + [0x0a] = SSE41_OP(roundss), + [0x0b] = SSE41_OP(roundsd), + [0x0c] = SSE41_OP(blendps), + [0x0d] = SSE41_OP(blendpd), + [0x0e] = SSE41_OP(pblendw), + [0x0f] = SSSE3_OP(palignr), + [0x14] = SSE41_SPECIAL, /* pextrb */ + [0x15] = SSE41_SPECIAL, /* pextrw */ + [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */ + [0x17] = SSE41_SPECIAL, /* extractps */ + [0x20] = SSE41_SPECIAL, /* pinsrb */ + [0x21] = SSE41_SPECIAL, /* insertps */ + [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */ + [0x40] = SSE41_OP(dpps), + [0x41] = SSE41_OP(dppd), + [0x42] = SSE41_OP(mpsadbw), + [0x60] = SSE42_OP(pcmpestrm), + [0x61] = SSE42_OP(pcmpestri), + [0x62] = SSE42_OP(pcmpistrm), + [0x63] = SSE42_OP(pcmpistri), +}; + static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) { int b1, op1_offset, op2_offset, is_xmm, val, ot; @@ -2960,7 +3044,8 @@ return; } if (is_xmm && !(s->flags & HF_OSFXSR_MASK)) - goto illegal_op; + if ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA)) + goto illegal_op; if (b == 0x0e) { if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) goto illegal_op; @@ -3426,6 +3511,7 @@ case 0x1c5: if (mod != 3) goto illegal_op; + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; val = ldub_code(s->pc++); if (b1) { val &= 7; @@ -3439,7 +3525,7 @@ offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); } reg = ((modrm >> 3) & 7) | rex_r; - gen_op_mov_reg_T0(OT_LONG, reg); + gen_op_mov_reg_T0(ot, reg); break; case 0x1d6: /* movq ea, xmm */ if (mod != 3) { @@ -3482,6 +3568,271 @@ reg = ((modrm >> 3) & 7) | rex_r; gen_op_mov_reg_T0(OT_LONG, reg); break; + case 0x138: + if (s->prefix & PREFIX_REPNZ) + goto crc32; + case 0x038: + b = modrm; + modrm = ldub_code(s->pc++); + rm = modrm & 7; + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + + sse_op2 = sse_op_table6[b].op[b1]; + if (!sse_op2) + goto illegal_op; + if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) + goto illegal_op; + + if (b1) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); + } else { + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + switch (b) { + case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */ + case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ + case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ + gen_ldq_env_A0(s->mem_index, op2_offset + + offsetof(XMMReg, XMM_Q(0))); + break; + case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ + case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ + tcg_gen_qemu_ld32u(cpu_tmp2_i32, cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset + + offsetof(XMMReg, XMM_L(0))); + break; + case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ + tcg_gen_qemu_ld16u(cpu_tmp0, cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset + + offsetof(XMMReg, XMM_W(0))); + break; + case 0x2a: /* movntqda */ + gen_ldo_env_A0(s->mem_index, op1_offset); + return; + default: + gen_ldo_env_A0(s->mem_index, op2_offset); + } + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } else { + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0(s->mem_index, op2_offset); + } + } + if (sse_op2 == SSE_SPECIAL) + goto illegal_op; + + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_helper_0_2(sse_op2, cpu_ptr0, cpu_ptr1); + + if (b == 0x17) + s->cc_op = CC_OP_EFLAGS; + break; + case 0x338: /* crc32 */ + crc32: + b = modrm; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + + if (b != 0xf0 && b != 0xf1) + goto illegal_op; + if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) + goto illegal_op; + + if (b == 0xf0) + ot = OT_BYTE; + else if (b == 0xf1 && s->dflag != 2) + if (s->prefix & PREFIX_DATA) + ot = OT_WORD; + else + ot = OT_LONG; + else + ot = OT_QUAD; + + gen_op_mov_TN_reg(OT_LONG, 0, reg); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + tcg_gen_helper_1_3(helper_crc32, cpu_T[0], cpu_tmp2_i32, + cpu_T[0], tcg_const_i32(8 << ot)); + + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; + gen_op_mov_reg_T0(ot, reg); + break; + case 0x03a: + case 0x13a: + b = modrm; + modrm = ldub_code(s->pc++); + rm = modrm & 7; + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + + sse_op2 = sse_op_table7[b].op[b1]; + if (!sse_op2) + goto illegal_op; + if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) + goto illegal_op; + + if (sse_op2 == SSE_SPECIAL) { + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + reg = ((modrm >> 3) & 7) | rex_r; + val = ldub_code(s->pc++); + switch (b) { + case 0x14: /* pextrb */ + tcg_gen_ld8u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_B(val & 15))); + if (mod == 3) + gen_op_mov_reg_T0(ot, rm); + else + tcg_gen_qemu_st8(cpu_T[0], cpu_A0, + (s->mem_index >> 2) - 1); + break; + case 0x15: /* pextrw */ + tcg_gen_ld16u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_W(val & 7))); + if (mod == 3) + gen_op_mov_reg_T0(ot, rm); + else + tcg_gen_qemu_st16(cpu_T[0], cpu_A0, + (s->mem_index >> 2) - 1); + break; + case 0x16: + if (ot == OT_LONG) { /* pextrd */ + tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].XMM_L(val & 3))); + if (mod == 3) + gen_op_mov_reg_v(ot, rm, cpu_tmp2_i32); + else + tcg_gen_qemu_st32(cpu_tmp2_i32, cpu_A0, + (s->mem_index >> 2) - 1); + } else { /* pextrq */ + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].XMM_Q(val & 1))); + if (mod == 3) + gen_op_mov_reg_v(ot, rm, cpu_tmp1_i64); + else + tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, + (s->mem_index >> 2) - 1); + } + break; + case 0x17: /* extractps */ + tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_L(val & 3))); + if (mod == 3) + gen_op_mov_reg_T0(ot, rm); + else + tcg_gen_qemu_st32(cpu_T[0], cpu_A0, + (s->mem_index >> 2) - 1); + break; + case 0x20: /* pinsrb */ + if (mod == 3) + gen_op_mov_TN_reg(OT_LONG, 0, rm); + else + tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st8_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_B(val & 15))); + break; + case 0x21: /* insertps */ + if (mod == 3) + tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, + offsetof(CPUX86State,xmm_regs[rm] + .XMM_L((val >> 6) & 3))); + else + tcg_gen_qemu_ld32u(cpu_tmp2_i32, cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, + offsetof(CPUX86State,xmm_regs[reg] + .XMM_L((val >> 4) & 3))); + if ((val >> 0) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_L(0))); + if ((val >> 1) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_L(1))); + if ((val >> 2) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_L(2))); + if ((val >> 3) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].XMM_L(3))); + break; + case 0x22: + if (ot == OT_LONG) { /* pinsrd */ + if (mod == 3) + gen_op_mov_v_reg(ot, cpu_tmp2_i32, rm); + else + tcg_gen_qemu_ld32u(cpu_tmp2_i32, cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].XMM_L(val & 3))); + } else { /* pinsrq */ + if (mod == 3) + gen_op_mov_v_reg(ot, cpu_tmp1_i64, rm); + else + tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, + (s->mem_index >> 2) - 1); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].XMM_Q(val & 1))); + } + break; + } + return; + } + + if (b1) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); + } else { + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldo_env_A0(s->mem_index, op2_offset); + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } else { + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0(s->mem_index, op2_offset); + } + } + val = ldub_code(s->pc++); + + if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ + s->cc_op = CC_OP_EFLAGS; + + if (s->dflag == 2) + /* The helper must use entire 64-bit gp registers */ + val |= 1 << 8; + } + + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_helper_0_3(sse_op2, cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); + break; default: goto illegal_op; } @@ -6401,7 +6752,8 @@ tcg_gen_helper_0_0(helper_rdpmc); break; case 0x134: /* sysenter */ - if (CODE64(s)) + /* For Intel SYSENTER is valid on 64-bit */ + if (CODE64(s) && cpu_single_env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) goto illegal_op; if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); @@ -6416,7 +6768,8 @@ } break; case 0x135: /* sysexit */ - if (CODE64(s)) + /* For Intel SYSEXIT is valid on 64-bit */ + if (CODE64(s) && cpu_single_env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) goto illegal_op; if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); @@ -6426,7 +6779,7 @@ s->cc_op = CC_OP_DYNAMIC; } gen_jmp_im(pc_start - s->cs_base); - tcg_gen_helper_0_0(helper_sysexit); + tcg_gen_helper_0_1(helper_sysexit, tcg_const_i32(dflag)); gen_eob(s); } break; @@ -6987,7 +7340,7 @@ gen_eob(s); } break; - /* MMX/3DNow!/SSE/SSE2/SSE3 support */ + /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ case 0x1c3: /* MOVNTI reg, mem */ if (!(s->cpuid_features & CPUID_SSE2)) goto illegal_op; @@ -7095,11 +7448,36 @@ tcg_gen_helper_0_0(helper_rsm); gen_eob(s); break; + case 0x1b8: /* SSE4.2 popcnt */ + if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != + PREFIX_REPZ) + goto illegal_op; + if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) + goto illegal_op; + + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7); + + if (s->prefix & PREFIX_DATA) + ot = OT_WORD; + else if (s->dflag != 2) + ot = OT_LONG; + else + ot = OT_QUAD; + + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + tcg_gen_helper_1_2(helper_popcnt, + cpu_T[0], cpu_T[0], tcg_const_i32(ot)); + gen_op_mov_reg_T0(ot, reg); + + s->cc_op = CC_OP_EFLAGS; + break; case 0x10e ... 0x10f: /* 3DNow! instructions, ignore prefixes */ s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); case 0x110 ... 0x117: case 0x128 ... 0x12f: + case 0x138 ... 0x13a: case 0x150 ... 0x177: case 0x17c ... 0x17f: case 0x1c2: diff -Nur kvm-76/qemu/target-ia64/cpu.h kvm-userspace/qemu/target-ia64/cpu.h --- kvm-76/qemu/target-ia64/cpu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ia64/cpu.h 2008-10-12 09:38:24.000000000 +0200 @@ -26,7 +26,6 @@ #include "ia64intrin.h" #include -#include #define TARGET_LONG_BITS 64 @@ -52,12 +51,9 @@ #define cpu_init cpu_ia64_init #define cpu_signal_handler cpu_ia64_signal_handler -struct CPUIA64State *env; +extern struct CPUIA64State *env; int cpu_get_pic_interrupt(CPUIA64State *s); int cpu_exec(CPUState *env1); -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), - int flags); CPUState *cpu_ia64_init(const char * cpu_model); static inline int cpu_mmu_index (CPUState *env) diff -Nur kvm-76/qemu/target-ia64/exec.h kvm-userspace/qemu/target-ia64/exec.h --- kvm-76/qemu/target-ia64/exec.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ia64/exec.h 2008-10-12 09:38:24.000000000 +0200 @@ -18,13 +18,21 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef __IA64_H__ +#define __IA64_H__ + //#include "dyngen-exec.h" +#include "config.h" + +#include "dyngen-exec.h" #include "cpu.h" #include "exec-all.h" #define tcg_qemu_tb_exec(tb_ptr) 0 +register struct CPUIA64State *env asm(AREG0); + static inline void env_to_regs(void) { } @@ -45,3 +53,5 @@ return 0; return EXCP_HALTED; } + +#endif diff -Nur kvm-76/qemu/target-ia64/fake-exec.c kvm-userspace/qemu/target-ia64/fake-exec.c --- kvm-76/qemu/target-ia64/fake-exec.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ia64/fake-exec.c 2008-10-12 09:38:24.000000000 +0200 @@ -14,6 +14,8 @@ * This work is licensed under the GNU GPL licence version 2 or later. * */ +#include + #include "cpu.h" #include "exec-all.h" diff -Nur kvm-76/qemu/target-ia64/firmware.c kvm-userspace/qemu/target-ia64/firmware.c --- kvm-76/qemu/target-ia64/firmware.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ia64/firmware.c 2008-10-12 09:38:24.000000000 +0200 @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff -Nur kvm-76/qemu/target-ppc/op.c kvm-userspace/qemu/target-ppc/op.c --- kvm-76/qemu/target-ppc/op.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ppc/op.c 2008-10-12 09:38:24.000000000 +0200 @@ -640,8 +640,6 @@ T0 += xer_ca + (-1); if (likely((uint32_t)T1 != 0)) xer_ca = 1; - else - xer_ca = 0; RETURN(); } @@ -651,8 +649,6 @@ T0 += xer_ca + (-1); if (likely((uint64_t)T1 != 0)) xer_ca = 1; - else - xer_ca = 0; RETURN(); } #endif @@ -798,7 +794,11 @@ /* multiply low word */ void OPPROTO op_mullw (void) { +#if defined(TARGET_PPC64) + T0 = (int64_t)(int32_t)T0 * (int64_t)(int32_t)T1; +#else T0 = (int32_t)(T0 * T1); +#endif RETURN(); } @@ -926,8 +926,6 @@ T0 = ~T0 + xer_ca - 1; if (likely((uint32_t)T0 != UINT32_MAX)) xer_ca = 1; - else - xer_ca = 0; RETURN(); } @@ -937,8 +935,6 @@ T0 = ~T0 + xer_ca - 1; if (likely((uint64_t)T0 != UINT64_MAX)) xer_ca = 1; - else - xer_ca = 0; RETURN(); } #endif diff -Nur kvm-76/qemu/target-ppc/op_helper.c kvm-userspace/qemu/target-ppc/op_helper.c --- kvm-76/qemu/target-ppc/op_helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ppc/op_helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -151,10 +151,8 @@ T0 += xer_ca + (-1); xer_ov = ((uint32_t)T1 & ((uint32_t)T1 ^ (uint32_t)T0)) >> 31; xer_so |= xer_ov; - if (likely(T1 != 0)) + if (likely((uint32_t)T1 != 0)) xer_ca = 1; - else - xer_ca = 0; } #if defined(TARGET_PPC64) @@ -164,10 +162,8 @@ T0 += xer_ca + (-1); xer_ov = ((uint64_t)T1 & ((uint64_t)T1 ^ (uint64_t)T0)) >> 63; xer_so |= xer_ov; - if (likely(T1 != 0)) + if (likely((uint64_t)T1 != 0)) xer_ca = 1; - else - xer_ca = 0; } #endif @@ -227,7 +223,7 @@ void do_mullwo (void) { - int64_t res = (int64_t)T0 * (int64_t)T1; + int64_t res = (int64_t)(int32_t)T0 * (int64_t)(int32_t)T1; if (likely((int32_t)res == res)) { xer_ov = 0; @@ -312,8 +308,6 @@ xer_so |= xer_ov; if (likely((uint32_t)T1 != UINT32_MAX)) xer_ca = 1; - else - xer_ca = 0; } #if defined(TARGET_PPC64) @@ -325,8 +319,6 @@ xer_so |= xer_ov; if (likely((uint64_t)T1 != UINT64_MAX)) xer_ca = 1; - else - xer_ca = 0; } #endif diff -Nur kvm-76/qemu/target-ppc/translate.c kvm-userspace/qemu/target-ppc/translate.c --- kvm-76/qemu/target-ppc/translate.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-ppc/translate.c 2008-10-12 09:38:24.000000000 +0200 @@ -3048,10 +3048,6 @@ #endif gen_op_btest_T1(ctx->nip); no_test: - if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) { - gen_update_nip(ctx, ctx->nip); - gen_op_debug(); - } tcg_gen_exit_tb(0); } } diff -Nur kvm-76/qemu/target-sparc/cpu.h kvm-userspace/qemu/target-sparc/cpu.h --- kvm-76/qemu/target-sparc/cpu.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/cpu.h 2008-10-12 09:38:24.000000000 +0200 @@ -340,6 +340,12 @@ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); void sparc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void cpu_lock(void); +void cpu_unlock(void); +int cpu_sparc_handle_mmu_fault(CPUSPARCState *env1, target_ulong address, int rw, + int mmu_idx, int is_softmmu); +target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev); +void dump_mmu(CPUSPARCState *env); /* translate.c */ void gen_intermediate_code_init(CPUSPARCState *env); @@ -492,4 +498,14 @@ #include "cpu-all.h" +/* sum4m.c, sun4u.c */ +void cpu_check_irqs(CPUSPARCState *env); + +#ifdef TARGET_SPARC64 +/* sun4u.c */ +void cpu_tick_set_count(void *opaque, uint64_t count); +uint64_t cpu_tick_get_count(void *opaque); +void cpu_tick_set_limit(void *opaque, uint64_t limit); +#endif + #endif diff -Nur kvm-76/qemu/target-sparc/exec.h kvm-userspace/qemu/target-sparc/exec.h --- kvm-76/qemu/target-sparc/exec.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/exec.h 2008-10-12 09:38:24.000000000 +0200 @@ -21,23 +21,12 @@ { } -/* helper.c */ -void cpu_lock(void); -void cpu_unlock(void); -int cpu_sparc_handle_mmu_fault(CPUState *env1, target_ulong address, int rw, - int mmu_idx, int is_softmmu); -target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev); -void dump_mmu(CPUState *env); - /* op_helper.c */ void do_interrupt(CPUState *env); /* cpu-exec.c */ void cpu_loop_exit(void); -/* sun4m.c */ -void cpu_check_irqs(CPUSPARCState *env); - static inline int cpu_halted(CPUState *env1) { if (!env1->halted) return 0; diff -Nur kvm-76/qemu/target-sparc/helper.c kvm-userspace/qemu/target-sparc/helper.c --- kvm-76/qemu/target-sparc/helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -31,7 +31,6 @@ //#define DEBUG_MMU //#define DEBUG_FEATURES -//#define DEBUG_PCALL static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model); @@ -659,13 +658,12 @@ #ifdef TARGET_SPARC64 env->pstate = PS_PRIV; env->hpstate = HS_PRIV; - env->pc = 0x1fff0000020ULL; // XXX should be different for system_reset env->tsptr = &env->ts[env->tl & MAXTL_MASK]; #else - env->pc = 0; env->mmuregs[0] &= ~(MMU_E | MMU_NF); env->mmuregs[0] |= env->def->mmu_bm; #endif + env->pc = 0; env->npc = env->pc + 4; #endif } @@ -1414,34 +1412,3 @@ #endif cpu_fprintf(f, "fsr: 0x%08x\n", env->fsr); } - -#ifdef TARGET_SPARC64 -#if !defined(CONFIG_USER_ONLY) -#include "qemu-common.h" -#include "hw/irq.h" -#include "qemu-timer.h" -#endif - -void helper_tick_set_count(void *opaque, uint64_t count) -{ -#if !defined(CONFIG_USER_ONLY) - ptimer_set_count(opaque, -count); -#endif -} - -uint64_t helper_tick_get_count(void *opaque) -{ -#if !defined(CONFIG_USER_ONLY) - return -ptimer_get_count(opaque); -#else - return 0; -#endif -} - -void helper_tick_set_limit(void *opaque, uint64_t limit) -{ -#if !defined(CONFIG_USER_ONLY) - ptimer_set_limit(opaque, -limit, 0); -#endif -} -#endif diff -Nur kvm-76/qemu/target-sparc/helper.h kvm-userspace/qemu/target-sparc/helper.h --- kvm-76/qemu/target-sparc/helper.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/helper.h 2008-10-12 09:38:24.000000000 +0200 @@ -38,8 +38,6 @@ DEF_HELPER(uint64_t, helper_tick_get_count, (void *opaque)) DEF_HELPER(void, helper_tick_set_limit, (void *opaque, uint64_t limit)) #endif -DEF_HELPER(void, helper_trap, (target_ulong nb_trap)) -DEF_HELPER(void, helper_trapcc, (target_ulong nb_trap, target_ulong do_trap)) DEF_HELPER(void, helper_check_align, (target_ulong addr, uint32_t align)) DEF_HELPER(void, helper_debug, (void)) DEF_HELPER(void, helper_save, (void)) diff -Nur kvm-76/qemu/target-sparc/machine.c kvm-userspace/qemu/target-sparc/machine.c --- kvm-76/qemu/target-sparc/machine.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/machine.c 2008-10-12 09:38:24.000000000 +0200 @@ -9,6 +9,7 @@ #ifdef TARGET_SPARC64 qemu_register_machine(&sun4u_machine); qemu_register_machine(&sun4v_machine); + qemu_register_machine(&niagara_machine); #else qemu_register_machine(&ss5_machine); qemu_register_machine(&ss10_machine); diff -Nur kvm-76/qemu/target-sparc/op_helper.c kvm-userspace/qemu/target-sparc/op_helper.c --- kvm-76/qemu/target-sparc/op_helper.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/op_helper.c 2008-10-12 09:38:24.000000000 +0200 @@ -10,6 +10,7 @@ //#define DEBUG_UNALIGNED //#define DEBUG_UNASSIGNED //#define DEBUG_ASI +//#define DEBUG_PCALL #ifdef DEBUG_MMU #define DPRINTF_MMU(fmt, args...) \ @@ -54,20 +55,6 @@ cpu_loop_exit(); } -void helper_trap(target_ulong nb_trap) -{ - env->exception_index = TT_TRAP + (nb_trap & 0x7f); - cpu_loop_exit(); -} - -void helper_trapcc(target_ulong nb_trap, target_ulong do_trap) -{ - if (do_trap) { - env->exception_index = TT_TRAP + (nb_trap & 0x7f); - cpu_loop_exit(); - } -} - static inline void set_cwp(int new_cwp) { cpu_set_cwp(env, new_cwp); @@ -824,7 +811,7 @@ break; } DPRINTF_MXCC("asi = %d, size = %d, sign = %d, " - "addr = %08x -> ret = %08x," + "addr = %08x -> ret = %" PRIx64 "," "addr = %08x\n", asi, size, sign, last_addr, ret, addr); #ifdef DEBUG_MXCC dump_mxcc(env); @@ -1087,8 +1074,8 @@ size); break; } - DPRINTF_MXCC("asi = %d, size = %d, addr = %08x, val = %08x\n", asi, - size, addr, val); + DPRINTF_MXCC("asi = %d, size = %d, addr = %08x, val = %" PRIx64 "\n", + asi, size, addr, val); #ifdef DEBUG_MXCC dump_mxcc(env); #endif @@ -1512,6 +1499,8 @@ case 0x18: // As if user primary LE case 0x80: // Primary case 0x88: // Primary LE + case 0xe2: // UA2007 Primary block init + case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { if ((env->def->features & CPU_FEATURE_HYPV) && env->hpstate & HS_PRIV) { @@ -1784,6 +1773,8 @@ case 0x18: // As if user primary LE case 0x80: // Primary case 0x88: // Primary LE + case 0xe2: // UA2007 Primary block init + case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { if ((env->def->features & CPU_FEATURE_HYPV) && env->hpstate & HS_PRIV) { @@ -1950,6 +1941,8 @@ } case 0x55: // I-MMU data access { + // TODO: auto demap + unsigned int i = (addr >> 3) & 0x3f; env->itlb_tag[i] = env->immuregs[6]; @@ -1957,7 +1950,22 @@ return; } case 0x57: // I-MMU demap - // XXX + { + unsigned int i; + + for (i = 0; i < 64; i++) { + if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) { + target_ulong mask = 0xffffffffffffe000ULL; + + mask <<= 3 * ((env->itlb_tte[i] >> 61) & 3); + if ((val & mask) == (env->itlb_tag[i] & mask)) { + env->itlb_tag[i] = 0; + env->itlb_tte[i] = 0; + } + return; + } + } + } return; case 0x58: // D-MMU regs { @@ -2027,6 +2035,23 @@ return; } case 0x5f: // D-MMU demap + { + unsigned int i; + + for (i = 0; i < 64; i++) { + if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) { + target_ulong mask = 0xffffffffffffe000ULL; + + mask <<= 3 * ((env->dtlb_tte[i] >> 61) & 3); + if ((val & mask) == (env->dtlb_tag[i] & mask)) { + env->dtlb_tag[i] = 0; + env->dtlb_tte[i] = 0; + } + return; + } + } + } + return; case 0x49: // Interrupt data receive // XXX return; @@ -2159,6 +2184,8 @@ helper_check_align(addr, 3); switch (asi) { + case 0xe0: // UA2007 Block commit store primary (cache flush) + case 0xe1: // UA2007 Block commit store secondary (cache flush) case 0xf0: // Block store primary case 0xf1: // Block store secondary case 0xf8: // Block store primary LE @@ -3059,3 +3086,27 @@ } #endif +#ifdef TARGET_SPARC64 +void helper_tick_set_count(void *opaque, uint64_t count) +{ +#if !defined(CONFIG_USER_ONLY) + cpu_tick_set_count(opaque, count); +#endif +} + +uint64_t helper_tick_get_count(void *opaque) +{ +#if !defined(CONFIG_USER_ONLY) + return cpu_tick_get_count(opaque); +#else + return 0; +#endif +} + +void helper_tick_set_limit(void *opaque, uint64_t limit) +{ +#if !defined(CONFIG_USER_ONLY) + cpu_tick_set_limit(opaque, limit); +#endif +} +#endif diff -Nur kvm-76/qemu/target-sparc/translate.c kvm-userspace/qemu/target-sparc/translate.c --- kvm-76/qemu/target-sparc/translate.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/target-sparc/translate.c 2008-10-12 09:38:24.000000000 +0200 @@ -93,6 +93,9 @@ #define QFPREG(r) (r & 0x1c) #endif +#define UA2005_HTRAP_MASK 0xff +#define V8_TRAP_MASK 0x7f + static int sign_extend(int x, int len) { len = 32 - len; @@ -2019,9 +2022,16 @@ cond = GET_FIELD(insn, 3, 6); if (cond == 0x8) { save_state(dc, cpu_cond); - tcg_gen_helper_0_1(helper_trap, cpu_dst); + if ((dc->def->features & CPU_FEATURE_HYPV) && + supervisor(dc)) + tcg_gen_andi_tl(cpu_dst, cpu_dst, UA2005_HTRAP_MASK); + else + tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); + tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); + tcg_gen_helper_0_1(raise_exception, cpu_dst); } else if (cond != 0) { TCGv r_cond = tcg_temp_new(TCG_TYPE_TL); + int l1; #ifdef TARGET_SPARC64 /* V9 icc/xcc */ int cc = GET_FIELD_SP(insn, 11, 12); @@ -2037,7 +2047,18 @@ save_state(dc, cpu_cond); gen_cond(r_cond, 0, cond); #endif - tcg_gen_helper_0_2(helper_trapcc, cpu_dst, r_cond); + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); + + if ((dc->def->features & CPU_FEATURE_HYPV) && + supervisor(dc)) + tcg_gen_andi_tl(cpu_dst, cpu_dst, UA2005_HTRAP_MASK); + else + tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); + tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); + tcg_gen_helper_0_1(raise_exception, cpu_dst); + + gen_set_label(l1); tcg_temp_free(r_cond); } gen_op_next_insn(); diff -Nur kvm-76/qemu/tcg/arm/tcg-target.c kvm-userspace/qemu/tcg/arm/tcg-target.c --- kvm-76/qemu/tcg/arm/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/arm/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -21,7 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", "%r1", "%r2", @@ -38,8 +40,9 @@ "%r13", "%r14", }; +#endif -int tcg_target_reg_alloc_order[] = { +static const int tcg_target_reg_alloc_order[] = { TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, @@ -57,10 +60,10 @@ TCG_REG_R14, }; -const int tcg_target_call_iarg_regs[4] = { +static const int tcg_target_call_iarg_regs[4] = { TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3 }; -const int tcg_target_call_oarg_regs[2] = { +static const int tcg_target_call_oarg_regs[2] = { TCG_REG_R0, TCG_REG_R1 }; @@ -91,7 +94,7 @@ } /* parse target specific constraints */ -int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) { const char *ct_str; diff -Nur kvm-76/qemu/tcg/hppa/tcg-target.c kvm-userspace/qemu/tcg/hppa/tcg-target.c --- kvm-76/qemu/tcg/hppa/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/hppa/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", "%r1", @@ -56,6 +57,7 @@ "%sp", "%r31", }; +#endif static const int tcg_target_reg_alloc_order[] = { TCG_REG_R4, @@ -106,7 +108,7 @@ } /* parse target specific constraints */ -int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) { const char *ct_str; diff -Nur kvm-76/qemu/tcg/i386/tcg-target.c kvm-userspace/qemu/tcg/i386/tcg-target.c --- kvm-76/qemu/tcg/i386/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/i386/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -21,7 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%eax", "%ecx", "%edx", @@ -31,8 +33,9 @@ "%esi", "%edi", }; +#endif -int tcg_target_reg_alloc_order[] = { +static const int tcg_target_reg_alloc_order[] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX, @@ -42,8 +45,8 @@ TCG_REG_EBP, }; -const int tcg_target_call_iarg_regs[3] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX }; -const int tcg_target_call_oarg_regs[2] = { TCG_REG_EAX, TCG_REG_EDX }; +static const int tcg_target_call_iarg_regs[3] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX }; +static const int tcg_target_call_oarg_regs[2] = { TCG_REG_EAX, TCG_REG_EDX }; static uint8_t *tb_ret_addr; @@ -80,7 +83,7 @@ } /* parse target specific constraints */ -int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) { const char *ct_str; diff -Nur kvm-76/qemu/tcg/ppc/tcg-target.c kvm-userspace/qemu/tcg/ppc/tcg-target.c --- kvm-76/qemu/tcg/ppc/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/ppc/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -39,6 +39,7 @@ #define ADDEND_OFFSET 4 #endif +#ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", @@ -73,6 +74,7 @@ "r30", "r31" }; +#endif static const int tcg_target_reg_alloc_order[] = { TCG_REG_R14, diff -Nur kvm-76/qemu/tcg/ppc64/tcg-target.c kvm-userspace/qemu/tcg/ppc64/tcg-target.c --- kvm-76/qemu/tcg/ppc64/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/ppc64/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -42,6 +42,7 @@ #define CMP_L (1<<21) #endif +#ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", @@ -76,6 +77,7 @@ "r30", "r31" }; +#endif static const int tcg_target_reg_alloc_order[] = { TCG_REG_R14, @@ -303,6 +305,7 @@ #define RLDICL XO30( 0) #define RLDICR XO30( 1) +#define RLDIMI XO30( 3) #define BCLR XO19( 16) #define BCCTR XO19(528) @@ -691,11 +694,10 @@ break; case 3: if (bswap) { - tcg_out32 (s, LWBRX | RT (0) | RB (r0)); - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, LWBRX | RT (data_reg) | RB (r1)); - tcg_out_rld (s, RLDICR, data_reg, data_reg, 32, 31); - tcg_out32 (s, OR | SAB (0, data_reg, data_reg)); + tcg_out_movi32 (s, 0, 4); + tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0)); + tcg_out32 (s, LWBRX | RT ( r1) | RA (r0)); + tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0); } else tcg_out32 (s, LD | RT (data_reg) | RA (r0)); break; diff -Nur kvm-76/qemu/tcg/sparc/tcg-target.c kvm-userspace/qemu/tcg/sparc/tcg-target.c --- kvm-76/qemu/tcg/sparc/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/sparc/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%g0", "%g1", @@ -56,6 +57,7 @@ "%i6", "%i7", }; +#endif static const int tcg_target_reg_alloc_order[] = { TCG_REG_L0, diff -Nur kvm-76/qemu/tcg/x86_64/tcg-target.c kvm-userspace/qemu/tcg/x86_64/tcg-target.c --- kvm-76/qemu/tcg/x86_64/tcg-target.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/tcg/x86_64/tcg-target.c 2008-10-12 09:38:24.000000000 +0200 @@ -21,7 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%rax", "%rcx", "%rdx", @@ -39,8 +41,9 @@ "%r14", "%r15", }; +#endif -int tcg_target_reg_alloc_order[] = { +static const int tcg_target_reg_alloc_order[] = { TCG_REG_RDI, TCG_REG_RSI, TCG_REG_RDX, @@ -59,7 +62,7 @@ TCG_REG_R15, }; -const int tcg_target_call_iarg_regs[6] = { +static const int tcg_target_call_iarg_regs[6] = { TCG_REG_RDI, TCG_REG_RSI, TCG_REG_RDX, @@ -68,7 +71,7 @@ TCG_REG_R9, }; -const int tcg_target_call_oarg_regs[2] = { +static const int tcg_target_call_oarg_regs[2] = { TCG_REG_RAX, TCG_REG_RDX }; diff -Nur kvm-76/qemu/tests/test-i386-ssse3.c kvm-userspace/qemu/tests/test-i386-ssse3.c --- kvm-76/qemu/tests/test-i386-ssse3.c 1970-01-01 01:00:00.000000000 +0100 +++ kvm-userspace/qemu/tests/test-i386-ssse3.c 2008-10-12 09:38:24.000000000 +0200 @@ -0,0 +1,57 @@ +/* See if various MMX/SSE SSSE3 instructions give expected results */ +#include +#include +#include + +int main(int argc, char *argv[]) { + char hello[16]; + const char ehlo[8] = "EHLO "; + uint64_t mask = 0x8080800302020001; + + uint64_t a = 0x0000000000090007; + uint64_t b = 0x0000000000000000; + uint32_t c; + uint16_t d; + + const char e[16] = "LLOaaaaaaaaaaaaa"; + const char f[16] = "aaaaaaaaaaaaaaHE"; + + /* pshufb mm1/xmm1, mm2/xmm2 */ + asm volatile ("movq (%0), %%mm0" : : "r" (ehlo) : "mm0", "mm1"); + asm volatile ("movq %0, %%mm1" : : "m" (mask)); + asm volatile ("pshufb %mm1, %mm0"); + asm volatile ("movq %%mm0, %0" : "=m" (hello)); + printf("%s\n", hello); + + /* pshufb mm1/xmm1, m64/m128 */ + asm volatile ("movq (%0), %%mm0" : : "r" (ehlo) : "mm0"); + asm volatile ("pshufb %0, %%mm0" : : "m" (mask)); + asm volatile ("movq %%mm0, %0" : "=m" (hello)); + printf("%s\n", hello); + + /* psubsw mm1/xmm1, m64/m128 */ + asm volatile ("movq %0, %%mm0" : : "r" (a) : "mm0"); + asm volatile ("phsubsw %0, %%mm0" : : "m" (b)); + asm volatile ("movq %%mm0, %0" : "=m" (a)); + printf("%i - %i = %i\n", 9, 7, -(int16_t) a); + + /* palignr mm1/xmm1, m64/m128, imm8 */ + asm volatile ("movdqa (%0), %%xmm0" : : "r" (e) : "xmm0"); + asm volatile ("palignr $14, (%0), %%xmm0" : : "r" (f)); + asm volatile ("movdqa %%xmm0, (%0)" : : "r" (hello)); + printf("%5.5s\n", hello); + +#if 1 /* SSE4 */ + /* popcnt r64, r/m64 */ + asm volatile ("movq $0x8421000010009c63, %%rax" : : : "rax"); + asm volatile ("popcnt %%ax, %%dx" : : : "dx"); + asm volatile ("popcnt %%eax, %%ecx" : : : "ecx"); + asm volatile ("popcnt %rax, %rax"); + asm volatile ("movq %%rax, %0" : "=m" (a)); + asm volatile ("movl %%ecx, %0" : "=m" (c)); + asm volatile ("movw %%dx, %0" : "=m" (d)); + printf("%i = %i\n%i = %i = %i\n", 13, (int) a, 9, c, d + 1); +#endif + + return 0; +} diff -Nur kvm-76/qemu/thunk.c kvm-userspace/qemu/thunk.c --- kvm-76/qemu/thunk.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/thunk.c 2008-10-12 09:38:24.000000000 +0200 @@ -113,7 +113,8 @@ } } -void thunk_register_struct_direct(int id, const char *name, StructEntry *se1) +void thunk_register_struct_direct(int id, const char *name, + const StructEntry *se1) { StructEntry *se; se = struct_entries + id; @@ -248,9 +249,9 @@ * between X86 and Alpha formats... */ unsigned int target_to_host_bitmask(unsigned int x86_mask, - bitmask_transtbl * trans_tbl) + const bitmask_transtbl * trans_tbl) { - bitmask_transtbl * btp; + const bitmask_transtbl *btp; unsigned int alpha_mask = 0; for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) { @@ -262,9 +263,9 @@ } unsigned int host_to_target_bitmask(unsigned int alpha_mask, - bitmask_transtbl * trans_tbl) + const bitmask_transtbl * trans_tbl) { - bitmask_transtbl * btp; + const bitmask_transtbl *btp; unsigned int x86_mask = 0; for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) { diff -Nur kvm-76/qemu/thunk.h kvm-userspace/qemu/thunk.h --- kvm-76/qemu/thunk.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/thunk.h 2008-10-12 09:38:24.000000000 +0200 @@ -68,7 +68,8 @@ } bitmask_transtbl; void thunk_register_struct(int id, const char *name, const argtype *types); -void thunk_register_struct_direct(int id, const char *name, StructEntry *se1); +void thunk_register_struct_direct(int id, const char *name, + const StructEntry *se1); const argtype *thunk_convert(void *dst, const void *src, const argtype *type_ptr, int to_host); #ifndef NO_THUNK_TYPE_SIZE @@ -154,8 +155,8 @@ #endif /* NO_THUNK_TYPE_SIZE */ unsigned int target_to_host_bitmask(unsigned int x86_mask, - bitmask_transtbl * trans_tbl); + const bitmask_transtbl * trans_tbl); unsigned int host_to_target_bitmask(unsigned int alpha_mask, - bitmask_transtbl * trans_tbl); + const bitmask_transtbl * trans_tbl); #endif diff -Nur kvm-76/qemu/vl.c kvm-userspace/qemu/vl.c --- kvm-76/qemu/vl.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/vl.c 2008-10-12 09:38:24.000000000 +0200 @@ -29,6 +29,7 @@ #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" +#include "hw/bt.h" #include "net.h" #include "console.h" #include "sysemu.h" @@ -73,7 +74,7 @@ #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) #include #else -#ifndef __sun__ +#ifdef __linux__ #include #include #include @@ -87,7 +88,8 @@ #include #include -#else +#endif +#ifdef __sun__ #include #include #include @@ -154,6 +156,8 @@ //#define DEBUG_UNUSED_IOPORT //#define DEBUG_IOPORT +//#define DEBUG_NET +//#define DEBUG_SLIRP #ifdef TARGET_PPC #define DEFAULT_RAM_SIZE 144 @@ -169,24 +173,24 @@ const char *bios_dir = CONFIG_QEMU_SHAREDIR; const char *bios_name = NULL; -void *ioport_opaque[MAX_IOPORTS]; -IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; -IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; +static void *ioport_opaque[MAX_IOPORTS]; +static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; +static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; /* Note: drives_table[MAX_DRIVES] is a dummy block driver if none available to store the VM snapshots */ DriveInfo drives_table[MAX_DRIVES+1]; int nb_drives; int extboot_drive = -1; /* point to the block driver where the snapshots are managed */ -BlockDriverState *bs_snapshots; -int vga_ram_size; +static BlockDriverState *bs_snapshots; +static int vga_ram_size; +enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; static DisplayState display_state; int nographic; -int curses; +static int curses; const char* keyboard_layout = NULL; int64_t ticks_per_sec; ram_addr_t ram_size; -int pit_min_timer_count = 0; int nb_nics; NICInfo nd_table[MAX_NICS]; int vm_running; @@ -203,8 +207,8 @@ int graphic_height = 600; int graphic_depth = 15; #endif -int full_screen = 0; -int no_frame = 0; +static int full_screen = 0; +static int no_frame = 0; int no_quit = 0; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; @@ -235,7 +239,7 @@ const char *option_rom[MAX_OPTION_ROMS]; int nb_option_roms; int semihosting_enabled = 0; -int autostart = 1; +int autostart; int time_drift_fix = 0; unsigned int kvm_shadow_memory = 0; const char *mem_path = NULL; @@ -262,8 +266,8 @@ #define MAX_ICOUNT_SHIFT 10 /* Compensate for varying guest execution speed. */ static int64_t qemu_icount_bias; -QEMUTimer *icount_rt_timer; -QEMUTimer *icount_vm_timer; +static QEMUTimer *icount_rt_timer; +static QEMUTimer *icount_vm_timer; uint8_t qemu_uuid[16]; @@ -1727,7 +1731,7 @@ static void init_timer_alarm(void) { - struct qemu_alarm_timer *t; + struct qemu_alarm_timer *t = NULL; int i, err = -1; for (i = 0; alarm_timers[i].name; i++) { @@ -3907,7 +3911,7 @@ /***********************************************************/ /* network device redirectors */ -__attribute__ (( unused )) +#if defined(DEBUG_NET) || defined(DEBUG_SLIRP) static void hex_dump(FILE *f, const uint8_t *buf, int size) { int len, i, j, c; @@ -3933,6 +3937,7 @@ fprintf(f, "\n"); } } +#endif static int parse_macaddr(uint8_t *macaddr, const char *p) { @@ -4152,7 +4157,7 @@ VLANClientState *vc; int ret = -EAGAIN; -#if 0 +#ifdef DEBUG_NET printf("vlan %d send:\n", vlan->id); hex_dump(stdout, buf, size); #endif @@ -4226,7 +4231,7 @@ void slirp_output(const uint8_t *pkt, int pkt_len) { -#if 0 +#ifdef DEBUG_SLIRP printf("slirp output:\n"); hex_dump(stdout, pkt, pkt_len); #endif @@ -4237,7 +4242,7 @@ static void slirp_receive(void *opaque, const uint8_t *buf, int size) { -#if 0 +#ifdef DEBUG_SLIRP printf("slirp input:\n"); hex_dump(stdout, buf, size); #endif @@ -4310,7 +4315,7 @@ #ifndef _WIN32 -char smb_dir[1024]; +static char smb_dir[1024]; static void erase_dir(char *dir_name) { @@ -5693,6 +5698,61 @@ } } +/***********************************************************/ +/* Bluetooth support */ +static int nb_hcis; +static int cur_hci; +static struct HCIInfo *hci_table[MAX_NICS]; +static struct bt_vlan_s { + struct bt_scatternet_s net; + int id; + struct bt_vlan_s *next; +} *first_bt_vlan; + +/* find or alloc a new bluetooth "VLAN" */ +static struct bt_scatternet_s *qemu_find_bt_vlan(int id) +{ + struct bt_vlan_s **pvlan, *vlan; + for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { + if (vlan->id == id) + return &vlan->net; + } + vlan = qemu_mallocz(sizeof(struct bt_vlan_s)); + vlan->id = id; + pvlan = &first_bt_vlan; + while (*pvlan != NULL) + pvlan = &(*pvlan)->next; + *pvlan = vlan; + return &vlan->net; +} + +static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) +{ +} + +static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) +{ + return -ENOTSUP; +} + +static struct HCIInfo null_hci = { + .cmd_send = null_hci_send, + .sco_send = null_hci_send, + .acl_send = null_hci_send, + .bdaddr_set = null_hci_addr_set, +}; + +struct HCIInfo *qemu_next_hci(void) +{ + if (cur_hci == nb_hcis) + return &null_hci; + + return hci_table[cur_hci++]; +} + +/***********************************************************/ +/* QEMU Block devices */ + #define HD_ALIAS "index=%d,media=disk" #ifdef TARGET_PPC #define CDROM_ALIAS "index=1,media=cdrom" @@ -5843,12 +5903,7 @@ index = -1; cache = 1; - if (!strcmp(machine->name, "realview") || - !strcmp(machine->name, "SS-5") || - !strcmp(machine->name, "SS-10") || - !strcmp(machine->name, "SS-600MP") || - !strcmp(machine->name, "versatilepb") || - !strcmp(machine->name, "versatileab")) { + if (machine->use_scsi) { type = IF_SCSI; max_devs = MAX_SCSI_DEVS; pstrcpy(devname, sizeof(devname), "scsi"); @@ -6591,6 +6646,7 @@ QEMUFilePutBufferFunc *put_buffer; QEMUFileGetBufferFunc *get_buffer; QEMUFileCloseFunc *close; + QEMUFileRateLimit *rate_limit; void *opaque; int64_t buf_offset; /* start of buffer when writing, end of buffer @@ -6603,62 +6659,104 @@ typedef struct QEMUFileFD { int fd; + QEMUFile *file; } QEMUFileFD; +static void fd_put_notify(void *opaque) +{ + QEMUFileFD *s = opaque; + + /* Remove writable callback and do a put notify */ + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + qemu_file_put_notify(s->file); +} + +static void fd_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) +{ + QEMUFileFD *s = opaque; + ssize_t len; + + do { + len = write(s->fd, buf, size); + } while (len == -1 && errno == EINTR); + + if (len == -1) + len = -errno; + + /* When the fd becomes writable again, register a callback to do + * a put notify */ + if (len == -EAGAIN) + qemu_set_fd_handler2(s->fd, NULL, NULL, fd_put_notify, s); +} + static int fd_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { QEMUFileFD *s = opaque; - int offset = 0; ssize_t len; -again: - len = read(s->fd, buf + offset, size - offset); - if (len == -1) { - if (errno == EINTR || errno == EAGAIN) - goto again; - } + do { + len = read(s->fd, buf, size); + } while (len == -1 && errno == EINTR); + + if (len == -1) + len = -errno; return len; } +static int fd_close(void *opaque) +{ + QEMUFileFD *s = opaque; + qemu_free(s); + return 0; +} + QEMUFile *qemu_fopen_fd(int fd) { QEMUFileFD *s = qemu_mallocz(sizeof(QEMUFileFD)); + + if (s == NULL) + return NULL; + s->fd = fd; - return qemu_fopen(s, NULL, fd_get_buffer, qemu_free); + s->file = qemu_fopen_ops(s, fd_put_buffer, fd_get_buffer, fd_close, NULL); + return s->file; } -typedef struct QEMUFileUnix +typedef struct QEMUFileStdio { FILE *outfile; -} QEMUFileUnix; +} QEMUFileStdio; -static void file_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +static void file_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) { - QEMUFileUnix *s = opaque; + QEMUFileStdio *s = opaque; fseek(s->outfile, pos, SEEK_SET); fwrite(buf, 1, size, s->outfile); } static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { - QEMUFileUnix *s = opaque; + QEMUFileStdio *s = opaque; fseek(s->outfile, pos, SEEK_SET); return fread(buf, 1, size, s->outfile); } -static void file_close(void *opaque) +static int file_close(void *opaque) { - QEMUFileUnix *s = opaque; + QEMUFileStdio *s = opaque; fclose(s->outfile); qemu_free(s); + return 0; } -QEMUFile *qemu_fopen_file(const char *filename, const char *mode) +QEMUFile *qemu_fopen(const char *filename, const char *mode) { - QEMUFileUnix *s; + QEMUFileStdio *s; - s = qemu_mallocz(sizeof(QEMUFileUnix)); + s = qemu_mallocz(sizeof(QEMUFileStdio)); if (!s) return NULL; @@ -6667,9 +6765,9 @@ goto fail; if (!strcmp(mode, "wb")) - return qemu_fopen(s, file_put_buffer, NULL, file_close); + return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL); else if (!strcmp(mode, "rb")) - return qemu_fopen(s, NULL, file_get_buffer, file_close); + return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL); fail: if (s->outfile) @@ -6684,7 +6782,8 @@ int64_t base_offset; } QEMUFileBdrv; -static void bdrv_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +static void bdrv_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) { QEMUFileBdrv *s = opaque; bdrv_pwrite(s->bs, s->base_offset + pos, buf, size); @@ -6696,7 +6795,14 @@ return bdrv_pread(s->bs, s->base_offset + pos, buf, size); } -QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) +static int bdrv_fclose(void *opaque) +{ + QEMUFileBdrv *s = opaque; + qemu_free(s); + return 0; +} + +static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) { QEMUFileBdrv *s; @@ -6708,24 +6814,27 @@ s->base_offset = offset; if (is_writable) - return qemu_fopen(s, bdrv_put_buffer, NULL, qemu_free); + return qemu_fopen_ops(s, bdrv_put_buffer, NULL, bdrv_fclose, NULL); - return qemu_fopen(s, NULL, bdrv_get_buffer, qemu_free); + return qemu_fopen_ops(s, NULL, bdrv_get_buffer, bdrv_fclose, NULL); } -QEMUFile *qemu_fopen(void *opaque, QEMUFilePutBufferFunc *put_buffer, - QEMUFileGetBufferFunc *get_buffer, QEMUFileCloseFunc *close) +QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, + QEMUFileGetBufferFunc *get_buffer, + QEMUFileCloseFunc *close, + QEMUFileRateLimit *rate_limit) { QEMUFile *f; f = qemu_mallocz(sizeof(QEMUFile)); if (!f) - return NULL; + return NULL; f->opaque = opaque; f->put_buffer = put_buffer; f->get_buffer = get_buffer; f->close = close; + f->rate_limit = rate_limit; return f; } @@ -6736,7 +6845,7 @@ return; if (f->buf_index > 0) { - f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index); + f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index); f->buf_offset += f->buf_index; f->buf_index = 0; } @@ -6751,19 +6860,26 @@ len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE); if (len < 0) - len = 0; + len = 0; f->buf_index = 0; f->buf_size = len; f->buf_offset += len; } -void qemu_fclose(QEMUFile *f) +int qemu_fclose(QEMUFile *f) { + int ret = 0; qemu_fflush(f); if (f->close) - f->close(f->opaque); + ret = f->close(f->opaque); qemu_free(f); + return ret; +} + +void qemu_file_put_notify(QEMUFile *f) +{ + f->put_buffer(f->opaque, NULL, 0, 0); } void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) @@ -6848,6 +6964,14 @@ return pos; } +int qemu_file_rate_limit(QEMUFile *f) +{ + if (f->rate_limit) + return f->rate_limit(f->opaque); + + return 0; +} + void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); @@ -7829,7 +7953,7 @@ /***********************************************************/ /* machine registration */ -QEMUMachine *first_machine = NULL; +static QEMUMachine *first_machine = NULL; QEMUMachine *current_machine = NULL; int qemu_register_machine(QEMUMachine *m) @@ -8329,8 +8453,10 @@ timeout = 0; } } else { - if (shutdown_requested) + if (shutdown_requested) { + ret = EXCP_INTERRUPT; break; + } timeout = 10; } #ifdef CONFIG_PROFILER @@ -8391,6 +8517,8 @@ " use -soundhw ? to get the list of supported cards\n" " use -soundhw all to enable all of them\n" #endif + "-vga [std|cirrus|vmware]\n" + " select video card type\n" "-localtime set the real time clock to local time [default=utc]\n" "-full-screen start in full screen\n" #ifdef TARGET_I386 @@ -8476,8 +8604,6 @@ "-no-kvm-pit disable KVM kernel mode PIT\n" #endif #ifdef TARGET_I386 - "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n" - " (default is CL-GD5446 PCI VGA)\n" "-no-acpi disable ACPI\n" #endif #ifdef CONFIG_CURSES @@ -8571,10 +8697,8 @@ QEMU_OPTION_bios, QEMU_OPTION_k, QEMU_OPTION_localtime, - QEMU_OPTION_cirrusvga, - QEMU_OPTION_vmsvga, QEMU_OPTION_g, - QEMU_OPTION_std_vga, + QEMU_OPTION_vga, QEMU_OPTION_echr, QEMU_OPTION_monitor, QEMU_OPTION_serial, @@ -8624,7 +8748,7 @@ int index; } QEMUOption; -const QEMUOption qemu_options[] = { +static const QEMUOption qemu_options[] = { { "h", 0, QEMU_OPTION_h }, { "help", 0, QEMU_OPTION_h }, @@ -8691,7 +8815,7 @@ { "g", 1, QEMU_OPTION_g }, #endif { "localtime", 0, QEMU_OPTION_localtime }, - { "std-vga", 0, QEMU_OPTION_std_vga }, + { "vga", HAS_ARG, QEMU_OPTION_vga }, { "echr", HAS_ARG, QEMU_OPTION_echr }, { "monitor", HAS_ARG, QEMU_OPTION_monitor }, { "serial", HAS_ARG, QEMU_OPTION_serial }, @@ -8716,8 +8840,6 @@ /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, - { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, - { "vmwarevga", 0, QEMU_OPTION_vmsvga }, { "no-acpi", 0, QEMU_OPTION_no_acpi }, { "no-reboot", 0, QEMU_OPTION_no_reboot }, { "no-shutdown", 0, QEMU_OPTION_no_shutdown }, @@ -8919,6 +9041,39 @@ } #endif +static void select_vgahw (const char *p) +{ + const char *opts; + + if (strstart(p, "std", &opts)) { + cirrus_vga_enabled = 0; + vmsvga_enabled = 0; + } else if (strstart(p, "cirrus", &opts)) { + cirrus_vga_enabled = 1; + vmsvga_enabled = 0; + } else if (strstart(p, "vmware", &opts)) { + cirrus_vga_enabled = 0; + vmsvga_enabled = 1; + } else { + invalid_vga: + fprintf(stderr, "Unknown vga type: %s\n", p); + exit(1); + } + while (*opts) { + const char *nextopt; + + if (strstart(opts, ",retrace=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "dumb", &nextopt)) + vga_retrace_method = VGA_RETRACE_DUMB; + else if (strstart(opts, "precise", &nextopt)) + vga_retrace_method = VGA_RETRACE_PRECISE; + else goto invalid_vga; + } else goto invalid_vga; + opts = nextopt; + } +} + #ifdef _WIN32 static BOOL WINAPI qemu_ctrl_handler(DWORD type) { @@ -9175,7 +9330,8 @@ nb_nics = 0; tb_size = 0; - + autostart= 1; + optind = 1; for(;;) { if (optind >= argc) @@ -9450,7 +9606,7 @@ case QEMU_OPTION_d: { int mask; - CPULogItem *item; + const CPULogItem *item; mask = cpu_str_to_log_mask(optarg); if (!mask) { @@ -9486,17 +9642,8 @@ case QEMU_OPTION_localtime: rtc_utc = 0; break; - case QEMU_OPTION_cirrusvga: - cirrus_vga_enabled = 1; - vmsvga_enabled = 0; - break; - case QEMU_OPTION_vmsvga: - cirrus_vga_enabled = 0; - vmsvga_enabled = 1; - break; - case QEMU_OPTION_std_vga: - cirrus_vga_enabled = 0; - vmsvga_enabled = 0; + case QEMU_OPTION_vga: + select_vgahw (optarg); break; case QEMU_OPTION_g: { diff -Nur kvm-76/qemu/vnc_keysym.h kvm-userspace/qemu/vnc_keysym.h --- kvm-76/qemu/vnc_keysym.h 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/qemu/vnc_keysym.h 2008-10-12 09:38:24.000000000 +0200 @@ -2,7 +2,7 @@ const char* name; int keysym; } name2keysym_t; -static name2keysym_t name2keysym[]={ +static const name2keysym_t name2keysym[]={ /* ascii */ { "space", 0x020}, { "exclam", 0x021}, diff -Nur kvm-76/user/kvmtrace.c kvm-userspace/user/kvmtrace.c --- kvm-76/user/kvmtrace.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/user/kvmtrace.c 2008-10-12 09:38:24.000000000 +0200 @@ -454,7 +454,8 @@ int i; for_each_tip(tip, i) { - (void) pthread_join(tip->thread, (void *) &ret); + if (tip->thread) + (void) pthread_join(tip->thread, (void *) &ret); close_thread(tip); } } diff -Nur kvm-76/user/main.c kvm-userspace/user/main.c --- kvm-76/user/main.c 2008-09-28 17:13:28.000000000 +0200 +++ kvm-userspace/user/main.c 2008-10-12 09:38:24.000000000 +0200 @@ -309,6 +309,11 @@ return 0; } +static int test_try_push_nmi(void *opaque) +{ + return 0; +} + static void test_post_kvm_run(void *opaque, int vcpu) { } @@ -347,6 +352,7 @@ .halt = test_halt, .io_window = test_io_window, .try_push_interrupts = test_try_push_interrupts, + .try_push_nmi = test_try_push_nmi, .post_kvm_run = test_post_kvm_run, .pre_kvm_run = test_pre_kvm_run, };