I'm working on writing own little os. I already done booting, entering protected mode, some text printing and serial communication with qemu.
I've been trying to add interrupts for over two days. I was looking everywhere including other systems sources. No I've got code below and qemu (I don't know why) shuts down. I including geust errors from qemu.
This is my main file kernel.cpp
__asm__ (".pushsection .text.start\r\n" \
"jmp main\r\n" \
".popsection\r\n");
#include <stdbool.h>
#include "utils/debug.h"
#include "drivers/display.h"
#include "drivers/serial.h"
#include "drivers/keyboard.h"
#include "drivers/pic.h"
#include "interrupt/interrupt.h"
void initDrivers(){
serial::init();
pic::init();
interrupt::init();
interrupt::enable();
terminal_initialize();
}
int main() {
initDrivers();
terminal_setcolor(VGA_COLOR_WHITE);
terminal_writestring("Hello!");
debug::println("after println");
bool alive = true;
while(alive) {
}
return 0;
}
PIC driver pic.cpp and pic.h
#include "pic.h"
#include <stddef.h>
#include <stdint.h>
#include "IO.h"
#include "../utils/debug.h"
/*
* IMR - Interrupt Mask Register
* IRR - Interrupt Request Register
* ISR - In-Service Register or Interrupt Service Routine
*
*/
namespace pic {
static const uint16_t PIC1_PORT = 0x20; // I/O offset address for master PIC
static const uint16_t PIC2_PORT = 0xA0; // I/O offset address for slave PIC
static const uint16_t PIC1_SPORT = 0x21; // I/O offset address for master PIC data
static const uint16_t PIC2_SPORT = 0xA1; // I/O offset address for slave PIC data
static const uint8_t OCW3_READ_IRR = 0x0A; // OCW3 irq ready next CMD read
static const uint8_t OCW3_READ_ISR = 0x0B; // OCW3 irq service next CMD read
void init(){
outb(PIC1_PORT, 0x11); // Sending ICW1 to first PIC (starting initialization)
io_wait(); // Waiting for PIC to process
outb(PIC2_PORT, 0x11); // Sending ICW1 to second PIC
io_wait(); // Waiting for PIC to process
outb(PIC1_SPORT, 0x20); // Sending ICW2 to first PIC (Mapping IRQs)
io_wait(); // Waiting for PIC to process
outb(PIC2_SPORT, 0x28); // Sending ICW2 to second PIC
io_wait(); // Waiting for PIC to process
outb(PIC1_SPORT, 4); // Sending ICW3 to first PIC, "there is a second one - slave" (0000 0100)
io_wait(); // Waiting for PIC to process
outb(PIC2_SPORT, 2); // Sending ICW3 to second PIC "You are slave" (0000 0010)
io_wait(); // Waiting for PIC to process
outb(PIC1_SPORT, 1); // Sending ICW4 to first PIC putting it into 80x86 mode (0000 0001)
io_wait(); // Waiting for PIC to process
outb(PIC2_SPORT, 1); // Sending ICW4 to second PIC putting it into 80x86 mode (0000 0001)
io_wait(); // Waiting for PIC to process
debug::println("PIC initialized!");
}
uint16_t __getIrqReg(uint8_t ocw3) {
outb(PIC1_SPORT, ocw3);
outb(PIC2_SPORT, ocw3);
return (inb(PIC2_SPORT) << 8) | inb(PIC1_SPORT);
}
uint16_t getIRR() {
return __getIrqReg(OCW3_READ_IRR);
}
uint16_t getISR() {
return __getIrqReg(OCW3_READ_ISR);
}
void ack(uint8_t irq) {
/* write EOI */
if (irq >= 16) return; // It's not a valid irq
if (irq >= 8) outb(PIC2_SPORT, 0x20); // It's a PIC2
// PIC1 EOI must be called anyway
outb(PIC1_SPORT, 0x20);
}
void mask(uint8_t irq, bool enable) {
uint16_t port;
if (irq < 8) port = PIC1_SPORT;
else if (irq < 16) {
port = PIC2_SPORT;
irq -= 8;
} else return;
uint8_t value = inb(port);
wait(150);
if (enable) value = value & ~(1 << irq);
else value = value | (1 << irq);
outb(port, value);
wait(150);
}
}
header:
#ifndef PIC_H
#define PIC_H
#include <stdint.h>
namespace pic {
void init();
uint16_t getIRR();
uint16_t getISR();
void ack(uint8_t irq);
void mask(uint8_t irq, bool enabled);
}
#endif
All setup for IDT idt.cpp
#include <stdint.h>
extern "C" {
// CPU Exceptions https://wiki.osdev.org/Exceptions
extern void isr_0(void); // Divide By Zero
extern void isr_1(void); // Debug
extern void isr_2(void); // Non-Maskable Interrupt
extern void isr_3(void); // Breakpoint
extern void isr_4(void); // Overflow
extern void isr_5(void); // Bound Range Exceeded
extern void isr_6(void); // Invalid Opcode
extern void isr_7(void); // Device Not Avaible
extern void isr_8(void); // Double Fault
extern void isr_9(void); // Coprocessor Segment Overrun (Old - out of use)
extern void isr_10(void); // Invalid TTS
extern void isr_11(void); // Segment Not Present
extern void isr_12(void); // Stack Segment Fault
extern void isr_13(void); // General Protection Fault
extern void isr_14(void); // Page Fault
extern void isr_16(void); // x87 Floating-Point Exception
extern void isr_17(void); // Aligment Check
extern void isr_18(void); // Machine Check
extern void isr_19(void); // SIMD Floating Point Exception
extern void isr_20(void); // Virtualization Exception
extern void isr_30(void); // Security Exception
// IRQs https://wiki.osdev.org/Interrupts#General_IBM-PC_Compatible_Interrupt_Information
extern void isr_32(void); // Programmable Interrupt Timer
extern void isr_33(void); // Keyboard
extern void isr_34(void); // Used Internally (Never Raised)
extern void isr_35(void); // COM2
extern void isr_36(void); // COM1
extern void isr_37(void); // LPT2
extern void isr_38(void); // Floppy Disk
extern void isr_39(void); // LPT1
extern void isr_40(void); // CMOS RTC
extern void isr_41(void); // Peripherals
extern void isr_42(void); // Peripherals
extern void isr_43(void); // Peripherals
extern void isr_44(void); // PS/2 Mouse
extern void isr_45(void); // FPU / Coprocessor / Inter-processor
extern void isr_46(void); // Primary ATA Hard Disk
extern void isr_47(void); // Seconadry ATA Hard Disk
extern void syscallHandler(void);
extern void isr_49(void);
extern void isr_50(void);
}
/* IDT - Interrupt Descriptor Table https://wiki.osdev.org/IDT
* offset - tells where Interrupt Service Routine Is Located
* selector - something like properties
* flags:
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* +---+---+---+---+---+---+---+---+
* | P | DPL | S | GateType |
* +---+---+---+---+---+---+---+---+
* P - Present (0 if unused)
* DPL - Sort of permission to call`
* Storage Segment - We want it 0
* GateType - Trap, Interrupt, Task
*/
struct idt_entry {
uint16_t offset_low;
uint16_t selector;
uint8_t unused;
uint8_t flags;
uint16_t offset_high;
};
#define IDT_ENTRY(offset, selector, flags) { \
(uint16_t)((uintptr_t)(offset) & 0xFFFF), \
(selector), \
0, \
(flags), \
(uint16_t)(((uintptr_t)(offset) >> 16) & 0xFFFF) \
}
#define IDT_INTERRUPT_GATE 0xE
#define IDT_TRAP_GATE 0xF
#define IDT_RING0 (0 << 5)
#define IDT_RING3 (3 << 5)
#define IDT_PRESENT (1 << 7)
extern "C" {
idt_entry idt[] = {
IDT_ENTRY(isr_0, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_1, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_2, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_3, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_4, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_5, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_6, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_7, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_8, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_9, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_10, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_11, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_12, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_13, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_14, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(isr_16, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_17, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_18, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_19, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_20, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(0, 0, 0),
IDT_ENTRY(isr_32, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_33, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_34, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_35, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_36, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_37, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_38, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_39, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_40, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_41, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_42, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_43, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_44, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_45, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_46, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
IDT_ENTRY(isr_47, 0x8, IDT_INTERRUPT_GATE | IDT_RING0 | IDT_PRESENT),
//IDT_ENTRY(syscallHandler, 0x8, IDT_TRAP_GATE | IDT_RING3 | IDT_PRESENT),
IDT_ENTRY(isr_49, 0x8, IDT_INTERRUPT_GATE | IDT_RING3 | IDT_PRESENT),
IDT_ENTRY(isr_50, 0x8, IDT_INTERRUPT_GATE | IDT_RING3 | IDT_PRESENT),
};
uint16_t idt_size = sizeof(idt) - 1;
}
File that glues that together interrupt.cpp and interrupt.h (In that file there is a function that causes crashes, but i will explain it after all files.)
#include "interrupt.h"
#include "signals.h"
#include <stdint.h>
#include "../utils/debug.h"
#include "../drivers/IO.h"
#define PIC1_COMMAND 0x20
#define PIC1_DATA 0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA 0xA1
#define PIC_EOI 0x20
#define EX_DEVIDE_BY_ZERO 0
#define EX_DEBUG 1
#define EX_NON_MASKABLE_INTERRUPT 2
#define EX_BREAKPOINT 3
#define EX_OVERFLOW 4
#define EX_BOUND_RANGE_EXCEEDED 5
#define EX_INVALID_OPCODE 6
#define EX_DEVICE_NOT_AVAILABLE 7
#define EX_DOUBLE_FAULT 8
#define EX_COPROCESSOR_SEGMENT_OVERRUN 9
#define EX_INVAILD_TSS 10
#define EX_SEGMENT_NOT_PRESENT 11
#define EX_STACK_SEGMENT_FAULT 12
#define EX_GENERAL_PROTECTION_FAULT 13
#define EX_PAGE_FAULT 14
#define EX_X87_FLOATING_POINT_EXCEPTION 16
#define EX_ALIGNMENT_CHECK 17
#define EX_MACHINE_CHECK 18
#define EX_SIMD_FLOATING_POINT_EXCEPTION 19
#define EX_VIRTUALIZATION_EXCEPTION 20
const char *exception_messages[] = {
"Division By Zero",
"Debug",
"Non Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segment Overrun",
"Bad TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"Unknown Interrupt",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved"
};
void (*interrupt::irqHandlers[16])(int) = {0};
void interrupt::init(){
__asm__ (
"push $idt \r\n" \
"pushw idt_size \r\n" \
"lidt (%esp) \r\n"
);
}
void interrupt::disable() {
asm volatile ("cli");
}
void interrupt::enable() {
asm volatile ("sti");
}
extern "C" {
volatile unsigned long signalPending = 0;
}
static bool handleUserspaceException(const InterruptContext* context) {
siginfo_t siginfo = {};
switch (context->interrupt) {
case EX_DEVIDE_BY_ZERO:
siginfo.si_signo = SIGFPE;
siginfo.si_code = FPE_INTDIV;
siginfo.si_addr = (void*) context->eip;
break;
case EX_DEBUG:
case EX_BREAKPOINT:
siginfo.si_signo = SIGTRAP;
siginfo.si_code = TRAP_BRKPT;
siginfo.si_addr = (void*) context->eip;
break;
case EX_OVERFLOW:
case EX_BOUND_RANGE_EXCEEDED:
case EX_STACK_SEGMENT_FAULT:
case EX_GENERAL_PROTECTION_FAULT:
siginfo.si_signo = SIGSEGV;
siginfo.si_code = SI_KERNEL;
siginfo.si_addr = (void*) context->eip;
break;
case EX_INVALID_OPCODE:
siginfo.si_signo = SIGILL;
siginfo.si_code = ILL_ILLOPC;
siginfo.si_addr = (void*) context->eip;
break;
case EX_PAGE_FAULT:
siginfo.si_signo = SIGSEGV;
siginfo.si_code = SEGV_MAPERR;
asm ("mov %%cr2, %0" : "=r"(siginfo.si_addr));
break;
case EX_X87_FLOATING_POINT_EXCEPTION:
case EX_SIMD_FLOATING_POINT_EXCEPTION:
siginfo.si_signo = SIGFPE;
siginfo.si_code = FPE_FLTINV;
siginfo.si_addr = (void*) context->eip;
break;
default:
return false;
}
//Process::current->raiseSignal(siginfo);
return true;
}
extern "C" InterruptContext* handleInterrupt(InterruptContext* context) {
InterruptContext* newContext = context;
if (context->interrupt <= 31 && context->cs != 0x8) {
if (!handleUserspaceException(context)) goto handleKernelException;
} else if (context->interrupt <= 31) { // CPU Exception
handleKernelException:
debug::print("Exception ");
debug::print(context->interrupt);
debug::print(" (");
debug::print(exception_messages[context->interrupt]);
debug::println(") occurred!");
debug::print("eax: 0x");
debug::print(context->eax);
debug::print(", ebx: 0x");
debug::print(context->ebx);
debug::print(", ecx: 0x");
debug::print(context->ecx);
debug::print(", edx: 0x");
debug::println(context->edx);
debug::print("edi: 0x");
debug::print(context->edi);
debug::print(", esi: 0x");
debug::print(context->esi);
debug::print(", ebp: 0x");
debug::print(context->ebp);
debug::print(", error: 0x");
debug::println(context->error);
debug::print("eip: 0x");
debug::print(context->eip);
debug::print(", cs: 0x");
debug::print(context->cs);
debug::print(", eflags: 0x");
debug::println(context->eflags);
if (context->cs != 0x8) {
debug::print("ss: 0x");
debug::print(context->ss);
debug::print(", esp: 0x");
debug::println(context->esp);
}
// Halt the cpu
while (true) asm volatile ("cli; hlt");
} else if (context->interrupt <= 47) { // IRQ
int irq = context->interrupt - 32;
if (irq == 0) {
//newContext = Process::schedule(context);
}
if (interrupt::irqHandlers[irq]) {
interrupt::irqHandlers[irq](irq);
}
// Send End of Interrupt
if (irq >= 8) {
outb(PIC2_COMMAND, PIC_EOI);
}
outb(PIC1_COMMAND, PIC_EOI);
} else if (context->interrupt == 0x32) {
//newContext = Signal::sigreturn(context);
debug::println("Keyboard!!");
} else {
debug::print("Unknown interrupt ");
debug::print(context->interrupt);
debug::println("!");
}
return newContext;
}
header:
#ifndef INTERRUPS_H
#define INTERRUPS_H
#include <stdint.h>
extern "C" volatile unsigned long signalPending;
namespace Signal {
static inline bool isPending() { return signalPending; }
}
struct InterruptContext {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t interrupt;
uint32_t error;
// These are pushed by the cpu when an interrupt happens.
uint32_t eip;
uint32_t cs;
uint32_t eflags;
// These are only valid if the interrupt came from Ring 3
uint32_t esp;
uint32_t ss;
};
union sigval {
int sival_int;
void* sival_ptr;
};
typedef __UINT64_TYPE__ __uid_t;
typedef struct {
int si_signo;
int si_code;
int si_pid;
__uid_t si_uid;
void* si_addr;
int si_status;
union sigval si_value;
} siginfo_t;
namespace interrupt {
extern void (*irqHandlers[])(int);
void init();
void disable();
void enable();
}
#endif
And finally Assembly code intr.s
.section .text
.macro isr no
.global isr_\no
isr_\no:
push $0 # no error code
push $\no
jmp commonHandler
.endm
.macro isr_error_code no
.global isr_\no
isr_\no:
# an error code was already pushed
push $\no
jmp commonHandler
.endm
commonHandler:
cld
# Push registers
push %ebp
push %edi
push %esi
push %edx
push %ecx
push %ebx
push %eax
# Switch to kernel data segment
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %esp, %eax
and $(~0xFF), %esp # Align the stack
sub $12, %esp
push %eax
call handleInterrupt
mov %eax, %esp
# Check whether signals are pending.
mov signalPending, %ebx
test %ebx, %ebx
jz 1f
# Don't handle signals when returning to kernelspace.
mov 40(%esp), %ebx # cs
cmp $0x8, %ebx
je 1f
and $(~0xFF), %esp # Align the stack
sub $12, %esp
push %eax
# call handleSignal
mov %eax, %esp
# Switch back to user data segment
1: mov $0x23, %ax
mov %ax, %ds
mov %ax, %es
pop %eax
pop %ebx
pop %ecx
pop %edx
pop %esi
pop %edi
pop %ebp
# Remove error code and interrupt number from stack
add $8, %esp
iret
# CPU Exceptions
isr 0 # Devide-by-zero Error
isr 1 # Debug
isr 2 # Non-maskable Interrupt
isr 3 # Breakpoint
isr 4 # Overflow
isr 5 # Bound Range Exceeded
isr 6 # Invalid Opcode
isr 7 # Device Not Available
isr_error_code 8 # Double Fault
isr 9 # Coprocessor Segment Overrun
isr_error_code 10 # Invalid TSS
isr_error_code 11 # Segment Not Present
isr_error_code 12 # Stack-Segement Fault
isr_error_code 13 # General Protection Fault
isr_error_code 14 # Page Fault
#isr 15 # Reserved
isr 16 # x87 Floating-Point Exception
isr_error_code 17 # Alignment Check
isr 18 # Machine Check
isr 19 # SIMD Floating-Point Exception
isr 20 # Virtualization Exception
# IRQs
isr 32
isr 33
isr 34
isr 35
isr 36
isr 37
isr 38
isr 39
isr 40
isr 41
isr 42
isr 43
isr 44
isr 45
isr 46
isr 47
#isr 48 # Syscall
isr 49 # Schedule
isr 50 # sigreturn
.global beginSigreturn
beginSigreturn:
# This section is mapped in all user address spaces. When a userspace
# program returns from a signal handler it will return to this address and
# and then perform a sigreturn.
int $0x32
.global endSigreturn
endSigreturn:
Now compile.sh that I'm using to macro compilation (I know about makefiles I will trying to involve them)
if [ $# -eq 0 ]; then
nasm -g -f elf32 -F dwarf -o boot.o bootloader/bootloader.asm
ld -melf_i386 -Ttext=0x7c00 -nostdlib --nmagic -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
g++ -std=c++14 -g -c -m32 -ffreestanding kernel.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding drivers/display.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding drivers/serial.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding drivers/keyboard.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding drivers/pic.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding interrupt/interrupt.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding interrupt/idt.cpp
g++ -std=c++14 -g -c -m32 -ffreestanding utils/debug.cpp
g++ -m32 -c interrupt/intr.s -o intr.o
ld -r -m elf_i386 kernel.o display.o serial.o keyboard.o pic.o interrupt.o idt.o intr.o debug.o -o main.o
ld -melf_i386 -Tlinker.ld -nostdlib --nmagic -o main.elf main.o
objcopy -O binary main.elf main.bin
dd if=/dev/zero of=disk.img bs=512 count=2880
dd if=boot.bin of=disk.img bs=512 conv=notrunc
dd if=main.bin of=disk.img bs=512 seek=1 conv=notrunc
rm -rf boot.o
rm -rf kernel.o
rm -rf serial.o
rm -rf main.o
rm -rf keyboard.o
rm -rf debug.o
rm -rf pic.o
rm -rf display.o
rm -rf boot.bin
rm -rf kernel.bin
else
if [ $1 = "clean" ]; then
rm -rf boot.o
echo successfully removed boot.o!
rm -rf kernel.o
echo successfully removed kernel.o!
rm -rf IO.o
echo successfully removed IO.o!
rm -rf main.o
echo successfully removed main.o!
rm -rf boot.bin
echo successfully removed boot.bin!
rm -rf kernel.bin
echo successfully removed kernel.bin!
rm -rf boot.elf
echo successfully removed boot.elf!
rm -rf kernel.elf
echo successfully removed kernel.elf!
rm -rf disk.img
echo successfully removed disk.img!
echo Cleanup done!
fi
fi
And the last ones run.sh and rund.sh that I'm using to macro qemu etc.
qemu-system-i386 -fda disk.img -d guest_errors & \
echo "Reading kernel log (Ctrl+C for exit)"
tail -f virtual.log
qemu-system-i386 -m 1024 -fda disk.img -S -s &
gdb main.elf \
-ex 'target remote localhost:1234' \
-ex 'layout src' \
-ex 'layout reg' \
-ex 'break main' \
-ex 'continue'
I'm including also qemu guest errors output
EAX=0000be44 EBX=0000be44 ECX=00090000 EDX=000003f8
ESI=00000000 EDI=00000000 EBP=bee00000 ESP=0008ffba
EIP=ffc80000 EFL=00000016 [----AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007d35 00000017
IDT= 0000bee0 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=0000268b CCD=0000be44 CCO=ADDL
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
So like I said. Code fragment that causes shutdowns is in interrupt.cpp
void interrupt::init(){
__asm__ (
"push $idt \r\n" \
"pushw idt_size \r\n" \
"lidt (%esp) \r\n"
);
}
If I comment this one, os working, but there is no interrupts.
I have no ideas how to fix it.
Any ideas?
The problem is that you don't restore the stack pointer in interrupt::init, so the return from that function returns into the weeds.
The simplest, but poor, fix for this is to simply add
add $6, %esp
after lidt.
A much better solution is to change it to not modify esp within the inline assembly.
void interrupt::init()
{
struct __attribute__((packed)) { uint16_t size; uint32_t idt; }
descr = { idt_size, idt };
__asm__ __volatile__ ("lidt %0" : : "m"(descr));
}
Although I appreciate your including all your code, I didn't carefully read all of it; I simply answered the first problem I noticed, so you may still have problems.
Related
in my kernel's arch/x86/entry/syscalls/syscall_64.tbl, there is its content:
# ...
447 common memfd_secret sys_memfd_secret
448 common process_mrelease sys_process_mrelease
449 common futex_waitv sys_futex_waitv
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
#
# Due to a historical design error, certain syscalls are numbered differently
# in x32 as compared to native x86_64. These syscalls have numbers 512-547.
# Do not add new syscalls to this range. Numbers 548 and above are available
# for non-x32 use.
#
512 x32 rt_sigaction compat_sys_rt_sigaction
513 x32 rt_sigreturn compat_sys_x32_rt_sigreturn
# ...
As you see, no system call's id is 451 and I want to create a linux module, which can add a system call, named sys_helloworld and its id is 451.
Following a book (the book is for kernel 4.16.10 but my kernel's version is 5.17.7), I write code below:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peterlits Zo 1927405082");
MODULE_DESCRIPTION("Just Hello World");
#define NUM 451
#define SYS_CALL_TABLE_ADDRESS 0xffffffffb1600240
#define HW_INFO "[helloworld INFO] "
int orig_cr0;
unsigned long *sys_call_table_my = NULL;
static int (*anything_saved)(void);
static int clear_cr0(void) {
unsigned int cr0 = 0;
unsigned int ret;
asm volatile("movq %%cr0, %%rax" : "=a"(cr0));
ret = cr0;
cr0 &= 0xfffffffffffeffff;
asm volatile("movq %%rax, %%cr0" :: "a"(cr0));
return ret;
}
static void setback_cr0(int val) {
asm volatile("movq %%rax, %%cr0" :: "a"(val));
}
// SYSCALL_DEFINE0(helloworld) { // sys_helloworld
asmlinkage long sys_helloworld(void) {
printk(HW_INFO "Hello world, from Peterlits Zo (1927405082)\n");
printk(HW_INFO "pid: %d, comm: %s\n", current->pid, current->comm);
return current->pid;
}
static int __init helloworld_init(void) {
printk(HW_INFO "Hello World From Module Helloworld by 1927405082.\n");
sys_call_table_my = (unsigned long*) (SYS_CALL_TABLE_ADDRESS);
anything_saved = (int (*)(void)) sys_call_table_my[NUM];
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long) &sys_helloworld;
setback_cr0(orig_cr0);
return 0;
}
static void __exit helloworld_exit(void) {
printk(HW_INFO "Byebye From Module Helloworld by 1927405082.\n");
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long) anything_saved;
setback_cr0(orig_cr0);
}
module_init(helloworld_init);
module_exit(helloworld_exit);
There are some macros:
NUM: The id of the new system call.
SYS_CALL_TABLE_ADDRESS: I run sudo cat /proc/kallsyms | grep sys_call_table to get its address.
It is easy to get its meaning:
Set the cr0 to make the kernel's memory to be writable.
Write the system call table.
Set back the cr0.
So I try to compile this module and install:
# In Makefile
obj-m += helloworld.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# In bash
$ make
$ sudo insmod helloworld.ko
zsh: killed sudo insmod helloworld.ko
I do not know why zsh want to kill my process, but the module is now in my kernel:
$ lsmod
Module Size Used by
helloworld 16384 1
efivarfs 16384 1
And I cannot remove it:
$ rmmod helloworld
rmmod: ERROR: Module helloworld is in use
$ rmmod -f helloworld
rmmod: ERROR: could not remove 'helloworld': Device or resourcee busy
rmmod: ERROR: could not remove module helloworld: Device or resource busy
But it looks failed and I cannot call the 451 system call:
long a = syscall(451); // -> -1
The log is:
May 20 13:24:56 PeterlitsArchVm kernel: [helloworld INFO] Hello World From Module Helloworld by 1927405082.
May 20 13:24:56 PeterlitsArchVm kernel: RIP: 0010:helloworld_init+0x17/0x1000 [helloworld]
May 20 13:24:56 PeterlitsArchVm kernel: Modules linked in: helloworld(O+) efivarfs
May 20 13:24:56 PeterlitsArchVm kernel: RIP: 0010:helloworld_init+0x17/0x1000 [helloworld]
May 20 13:27:25 PeterlitsArchVm dbus-daemon[234]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.10' (uid=0 pid=809 comm="sudo rmmod helloworld" label="kernel")
May 20 13:27:25 PeterlitsArchVm sudo[809]: peterlits : TTY=pts/0 ; PWD=/home/peterlits/proj/linux/01-01-模块,但是是HelloWorld ; USER=root ; COMMAND=/usr/bin/rmmod helloworld
May 20 13:28:10 PeterlitsArchVm dbus-daemon[234]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.11' (uid=0 pid=812 comm="sudo rmmod -f helloworld" label="kernel")
May 20 13:28:10 PeterlitsArchVm sudo[812]: peterlits : TTY=pts/0 ; PWD=/home/peterlits/proj/linux/01-01-模块,但是是HelloWorld ; USER=root ; COMMAND=/usr/bin/rmmod -f helloworld
And something looks not good (Why there is sys_call_table_my):
$ cat /proc/kallsyms | grep sys_call_table
0000000000000000 D sys_call_table
0000000000000000 D ia32_sys_call_table
0000000000000000 b sys_call_table_my [helloworld]
#craig-estey Hello, I try to use long for cr0, but it still do not work:
-int orig_cr0;
+long orig_cr0;
unsigned long *sys_call_table_my = NULL;
static int (*anything_saved)(void);
-static int clear_cr0(void) {
- unsigned int cr0 = 0;
- unsigned int ret;
+static long clear_cr0(void) {
+ unsigned long cr0 = 0;
+ unsigned long ret;
asm volatile("movq %%cr0, %%rax" : "=a"(cr0));
ret = cr0;
cr0 &= 0xfffffffffffeffff;
Im trying to write a basic page manager for my C kernel. The code goes like this:
#define NUM_PAGES 1024
#define PAGE_SIZE 4096
#define NULL 0
#define IMPORTANT_SEGMENT 0xC0900000
struct page {
void * addr;
int in_use;
};
struct page CORE_FILE[NUM_PAGES];
void mem_init() {
for (int i = 0; i < NUM_PAGES; i++) {
CORE_FILE[i].addr = (void*)IMPORTANT_SEGMENT+PAGE_SIZE*i;
CORE_FILE[i].in_use = 0;
}
}
void * allocate() {
for (int i = 0; i < NUM_PAGES; i++) {
if (!CORE_FILE[i].in_use) {
CORE_FILE[i].in_use = 1;
return CORE_FILE[i].addr;
}
}
return NULL;
}
int deallocate(void* p) {
for (int i = 0; i < NUM_PAGES; i++) {
if (CORE_FILE[i].addr == p && CORE_FILE[i].in_use) {
CORE_FILE[i].in_use = 0;
return 0;
}
}
return -1;
}
CORE_FILE is an array of structs containing just one field for telling if the page is in use and an address (im using contiguous addresses growing from IMPORTANT_SEGMENT = 0xC0900000).
When i call allocate() it returns me the correct void* which i cast for example to char, but when i write to the address it simply does nothing.
I have checked the address to which is pointing with GDB and is the correct one.
But when i examine its contents they haven't been updated (Still 0).
void kmain(void) {
mem_init();
int * addr = (int*)allocate();
*addr = 5;
}
Im giving qemu 4 GB of RAM executing with:
qemu-system-i386 -m 4G -kernel kernel -gdb tcp::5022
Perhaps im writing to non-existent memory or maybe im overwriting the address contents after. I don't know.
Any ideas will be appreciated.
Thank you in advance.
[edit] This is the bootloader im using:
bits 32
section .text
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
global start
global keyboard_handler
global read_port
global write_port
global load_idt
extern kmain ;this is defined in the c file
extern keyboard_handler_main
read_port:
mov edx, [esp + 4]
;al is the lower 8 bits of eax
in al, dx ;dx is the lower 16 bits of edx
ret
write_port:
mov edx, [esp + 4]
mov al, [esp + 4 + 4]
out dx, al
ret
load_idt:
mov edx, [esp + 4]
lidt [edx]
sti ;turn on interrupts
ret
keyboard_handler:
call keyboard_handler_main
iretd
start:
cli ;block interrupts
mov esp, stack_space
call kmain
hlt ;halt the CPU
section .bss
resb 8192; 8KB for stack
stack_space:
My link.ld
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
. = 0x200000;
.data : { *(.data) }
. = 0x300000;
.bss : { *(.bss) }
}
Edit2: I compile with this
nasm -f elf32 kernel.asm -o kasm.o
gcc -g -fno-stack-protector -fno-builtin -m32 -c memory.c -o memory.o
gcc -g -fno-stack-protector -fno-builtin -m32 -c kernel.c -o kc.o
ld -m elf_i386 -T link.ld -o kernel kasm.o memory.o kc.o
The problem is with protected and real mode, when the computer boots it does so in 16 bit real mode, which makes you able to address 1 MB of data. Everything over that wont be suitable for reading/writing. If i changed the IMPORTANT_SEGMENT to 0x300000 it works.
Now i have to create and load my gdt, enable the a20 line, enable protected mode, set the registers and jump to my code.
Apparently, it is possible to atomically increment two integers with compare-and-swap instructions. This talk claims that such an algorithm exists but it does not detail what it looks like.
How can this be done?
(Note, that the obvious solution of incrementing the integers one after the other is not atomic. Also, stuffing multiple integers into one machine word does not count because it would restrict the possible range.)
Make me think of a sequence lock. Not very accurate (putting this from memory) but something along the lines of:
let x,y and s be 64 bit integers.
To increment:
atomic s++ (I mean atomic increment using 64 bit CAS op)
memory barrier
atomic x++
atomic y++
atomic s++
memory barrier
To read:
do {
S1 = load s
X = load x
Y = load y
memory barrier
S2 = load s
} while (S1 != S2)
Also see https://en.wikipedia.org/wiki/Seqlock
If sse2 is available, you can use paddq to add 2 64 bit integers to two other 64 bit integers in one instruction.
#include "emmintrin.h"
//initialize your values somewhere:
//const __m128i ones = _mm_set1_epi64x(1);
//volatile register __m128i vars =
// _mm_set_epi64x(24,7);
static inline __m128i inc_both(__m128i vars, __m128i ones){
return _mm_add_epi64(vars,ones);
}
This should compile to
paddq %xmm0, %xmm1
Since it is static inline, it may use other xmm registers though. If there is significant register pressure the ones operands may become ones(℅rip)
Note: this can be used for adding values other than 1 and there are similar operations for most other math, bitwise and compare instructions, should you need them.
So you can use the lock prefix and make it into an inline asm macro
#define inc64x2(vars) asm volatile( \
"paddq %0, %1\n":"+x"(vars):"x"(ones) \
);
The arm neon equivalent is something like: vaddq_s64(...), but there is a great article about arm/x86 equivalents here.
I've got a solution I've tested. Contained herein is a soup to nuts proof of concept program.
The algorithm is a "use CAS thread id gate" as the 3rd integer. I watched the video talk twice, and I believe this qualifies. It may not be the algorithm that the presenter was thinking of, but it does work.
The X and Y values can be anywhere in memory and the program places them far enough away from each other that they are on different cache lines. It doesn't really matter.
A quick description of the algorithm:
Each thread has a unique id number or tid (non-zero), taken from one's favorite source: pthead_t, getpid, gettid, make one up by whatever means you want. In the program, it just assigns them sequentially starting from 1.
Each thread will call the increment function with this number.
The increment function will spin on a global gate variable using CAS with an old value of 0 and a new value of tid.
When the CAS succeeds, the thread now "owns" things. In other words, if the gate is zero, it's up for grabs. A non-zero value is the tid of the owner and the gate is locked.
Now, the owner is free to increment the X and Y values with simple x += 1 and y += 1.
After that, the increment function releases by doing a store of 0 into the gate.
Here is the diagnostic/proof-of-concept program with everything. The algorithm itself has no restrictions, but I coded it for my machine.
Some caveats:
It assumes gcc/clang
It assumes a 64 bit x86_64 arch.
This was coded using nothing but inline asm and needs no [nor uses any] compiler atomic support for clarity, simplicity, and transparency.
This was built under linux, but should work on any "reasonable" x86 machine/OS (e.g. BSD, OSX should be fine, cygwin probably, and mingw maybe)
Other arches are fine if they support CAS, I just didn't code for them (e.g. arm might work if you code the CAS with ldex/stex pairs)
There are enough abstract primitives that this would/should be easy.
No attempt at Windows compatibility [if you want it, do your own port but send me no tears--or comments :-)].
The makefile and program have been defaulted to best values
Some x86 CPUs may need to use different defaults (e.g. need fence instructions). See the makefile.
Anyway, here it is:
// caslock -- prove cas lock algorithm
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#define systls __thread
// repeat the madness only once
#ifdef __clang__
#define inline_common inline
#else
#define inline_common static inline
#endif
#define inline_always inline_common __attribute__((__always_inline__))
#define inline_never __attribute__((__noinline__))
// WARNING: inline CAS fails for gcc but works for clang!
#if _USE_CASINLINE_
#define inline_cas inline_always
#else
#define inline_cas inline_never
#endif
typedef unsigned int u32;
typedef unsigned long long u64;
#ifndef LOOPMAX
#define LOOPMAX 1000000
#endif
#ifndef TIDMAX
#define TIDMAX 20
#endif
#if _USE_VPTR_
typedef volatile u32 *xptr32_p;
typedef volatile u64 *xptr64_p;
#else
typedef u32 *xptr32_p;
typedef u64 *xptr64_p;
#endif
#if _USE_TID64_
typedef u64 tid_t;
#define tidload(_xptr) loadu64(_xptr)
#define tidcas(_xptr,_oval,_nval) casu64(_xptr,_oval,_nval)
#define tidstore(_xptr,_nval) storeu64(_xptr,_nval)
#else
typedef u32 tid_t;
#define tidload(_xptr) loadu32(_xptr)
#define tidcas(_xptr,_oval,_nval) casu32(_xptr,_oval,_nval)
#define tidstore(_xptr,_nval) storeu32(_xptr,_nval)
#endif
tid_t tidgate; // gate control
tid_t readycnt; // number of threads ready
tid_t donecnt; // number of threads complete
// ensure that the variables are nowhere near each other
u64 ary[100];
#define kickoff ary[32] // sync to fire threads
#define xval ary[31] // the X value
#define yval ary[87] // the Y value
int inctype; // increment algorithm to use
tid_t tidmax; // maximum number of tasks
u64 loopmax; // loop maximum for each task
// task control
struct tsk {
tid_t tsk_tid; // task id
u32 tsk_casmiss; // cas miss count
};
typedef struct tsk tsk_t;
tsk_t *tsklist; // task list
systls tsk_t *tskcur; // current task block
// show progress
#define PGR(_pgr) \
do { \
fputs(_pgr,stdout); \
fflush(stdout); \
} while (0)
// NOTE: some x86 arches need fence instructions
// 0 -- no fence instructions
// 1 -- use mfence
// 2 -- use lfence/sfence
#if _USE_BARRIER_ == 0
#define BARRIER_RELEASE ""
#define BARRIER_ACQUIRE ""
#define BARRIER_ALL ""
#elif _USE_BARRIER_ == 1
#define BARRIER_ACQUIRE "\tmfence\n"
#define BARRIER_RELEASE "\tmfence\n"
#define BARRIER_ALL "\tmfence\n"
#elif _USE_BARRIER_ == 2
#define BARRIER_ACQUIRE "\tlfence\n"
#define BARRIER_RELEASE "\tsfence\n"
#define BARRIER_ALL "\tmfence\n"
#else
#error caslock: unknown barrier type
#endif
// barrier_acquire -- acquire barrier
inline_always void
barrier_acquire(void)
{
__asm__ __volatile__ (
BARRIER_ACQUIRE
:
:
: "memory");
}
// barrier_release -- release barrier
inline_always void
barrier_release(void)
{
__asm__ __volatile__ (
BARRIER_RELEASE
:
:
: "memory");
}
// barrier -- barrier
inline_always void
barrier(void)
{
__asm__ __volatile__ (
BARRIER_ALL
:
:
: "memory");
}
// casu32 -- compare and exchange four bytes
// RETURNS: 1=ok, 0=fail
inline_cas int
casu32(xptr32_p xptr,u32 oldval,u32 newval)
{
char ok;
__asm__ __volatile__ (
" lock\n"
" cmpxchg %[newval],%[xptr]\n"
" sete %[ok]\n"
: [ok] "=r" (ok),
[xptr] "=m" (*xptr)
: "a" (oldval),
[newval] "r" (newval)
: "memory");
return ok;
}
// casu64 -- compare and exchange eight bytes
// RETURNS: 1=ok, 0=fail
inline_cas int
casu64(xptr64_p xptr,u64 oldval,u64 newval)
{
char ok;
__asm__ __volatile__ (
" lock\n"
" cmpxchg %[newval],%[xptr]\n"
" sete %[ok]\n"
: [ok] "=r" (ok),
[xptr] "=m" (*xptr)
: "a" (oldval),
[newval] "r" (newval)
: "memory");
return ok;
}
// loadu32 -- load value with barrier
// RETURNS: loaded value
inline_always u32
loadu32(const xptr32_p xptr)
{
u32 val;
barrier_acquire();
val = *xptr;
return val;
}
// loadu64 -- load value with barrier
// RETURNS: loaded value
inline_always u64
loadu64(const xptr64_p xptr)
{
u64 val;
barrier_acquire();
val = *xptr;
return val;
}
// storeu32 -- store value with barrier
inline_always void
storeu32(xptr32_p xptr,u32 val)
{
*xptr = val;
barrier_release();
}
// storeu64 -- store value with barrier
inline_always void
storeu64(xptr64_p xptr,u64 val)
{
*xptr = val;
barrier_release();
}
// qsleep -- do a quick sleep
inline_always void
qsleep(int bigflg)
{
struct timespec ts;
if (bigflg) {
ts.tv_sec = 1;
ts.tv_nsec = 0;
}
else {
ts.tv_sec = 0;
ts.tv_nsec = 1000;
}
nanosleep(&ts,NULL);
}
// incby_tidgate -- increment by using thread id gate
void
incby_tidgate(tid_t tid)
// tid -- unique id for accessing entity (e.g. thread id)
{
tid_t *gptr;
tid_t oval;
gptr = &tidgate;
// acquire the gate
while (1) {
oval = 0;
// test mode -- just do a nop instead of CAS to prove diagnostic
#if _USE_CASOFF_
*gptr = oval;
break;
#else
if (tidcas(gptr,oval,tid))
break;
#endif
++tskcur->tsk_casmiss;
}
#if _USE_INCBARRIER_
barrier_acquire();
#endif
// increment the values
xval += 1;
yval += 1;
#if _USE_INCBARRIER_
barrier_release();
#endif
// release the gate
// NOTE: CAS will always provide a barrier
#if _USE_CASPOST_ && (_USE_CASOFF_ == 0)
oval = tidcas(gptr,tid,0);
#else
tidstore(gptr,0);
#endif
}
// tskcld -- child task
void *
tskcld(void *arg)
{
tid_t tid;
tid_t oval;
u64 loopcur;
tskcur = arg;
tid = tskcur->tsk_tid;
// tell master thread that we're fully ready
while (1) {
oval = tidload(&readycnt);
if (tidcas(&readycnt,oval,oval + 1))
break;
}
// wait until we're given the starting gun
while (1) {
if (loadu64(&kickoff))
break;
qsleep(0);
}
// do the increments
for (loopcur = loopmax; loopcur > 0; --loopcur)
incby_tidgate(tid);
barrier();
// tell master thread that we're fully complete
while (1) {
oval = tidload(&donecnt);
if (tidcas(&donecnt,oval,oval + 1))
break;
}
return (void *) 0;
}
// tskstart -- start a child task
void
tskstart(tid_t tid)
{
pthread_attr_t attr;
pthread_t thr;
int err;
tsk_t *tsk;
tsk = tsklist + tid;
tsk->tsk_tid = tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,1);
err = pthread_create(&thr,&attr,tskcld,tsk);
pthread_attr_destroy(&attr);
if (err)
printf("tskstart: error -- err=%d\n",err);
}
// tskall -- run a single test
void
tskall(void)
{
tid_t tidcur;
tsk_t *tsk;
u64 incmax;
u64 val;
int err;
xval = 0;
yval = 0;
kickoff = 0;
readycnt = 0;
donecnt = 0;
tidgate = 0;
// prealloc the task blocks
tsklist = calloc(tidmax + 1,sizeof(tsk_t));
// start all tasks
PGR(" St");
for (tidcur = 1; tidcur <= tidmax; ++tidcur)
tskstart(tidcur);
// wait for all tasks to be fully ready
PGR(" Sw");
while (1) {
if (tidload(&readycnt) == tidmax)
break;
qsleep(1);
}
// the starting gun -- all tasks are waiting for this
PGR(" Ko");
storeu64(&kickoff,1);
// wait for all tasks to be fully done
PGR(" Wd");
while (1) {
if (tidload(&donecnt) == tidmax)
break;
qsleep(1);
}
PGR(" Done\n");
// check the final count
incmax = loopmax * tidmax;
// show per-task statistics
for (tidcur = 1; tidcur <= tidmax; ++tidcur) {
tsk = tsklist + tidcur;
printf("tskall: tsk=%llu tsk_casmiss=%d (%.3f%%)\n",
(u64) tidcur,tsk->tsk_casmiss,(double) tsk->tsk_casmiss / loopmax);
}
err = 0;
// check for failure
val = loadu64(&xval);
if (val != incmax) {
printf("tskall: xval fault -- xval=%lld incmax=%lld\n",val,incmax);
err = 1;
}
// check for failure
val = loadu64(&yval);
if (val != incmax) {
printf("tskall: yval fault -- yval=%lld incmax=%lld\n",val,incmax);
err = 1;
}
if (! err)
printf("tskall: SUCCESS\n");
free(tsklist);
}
// main -- master control
int
main(void)
{
loopmax = LOOPMAX;
tidmax = TIDMAX;
inctype = 0;
tskall();
return 0;
}
Here is the Makefile. Sorry for the extra boilerplate:
# caslock/Makefile -- make file for caslock
#
# options:
# LOOPMAX -- maximum loops / thread
#
# TIDMAX -- maximum number of threads
#
# BARRIER -- generate fence/barrier instructions
# 0 -- none
# 1 -- use mfence everywhere
# 2 -- use lfence for acquire, sfence for release
#
# CASOFF -- disable CAS to prove diagnostic works
# 0 -- normal mode
# 1 -- inhibit CAS during X/Y increment
#
# CASINLINE -- inline the CAS functions
# 0 -- do _not_ inline
# 1 -- inline them (WARNING: this fails for gcc but works for clang!)
#
# CASPOST -- increment gate release mode
# 0 -- use fenced store
# 1 -- use CAS store (NOTE: not really required)
#
# INCBARRIER -- use extra barriers around increments
# 0 -- rely on CAS for barrier
# 1 -- add extra safety barriers immediately before increment of X/Y
#
# TID64 -- use 64 bit thread "id"s
# 0 -- use 32 bit
# 1 -- use 64 bit
#
# VPTR -- use volatile pointers in function definitions
# 0 -- use ordinary pointers
# 1 -- use volatile pointers (NOTE: not really required)
ifndef _CASLOCK_MK_
_CASLOCK_MK_ = 1
OLIST += caslock.o
ifndef LOOPMAX
LOOPMAX = 1000000
endif
ifndef TIDMAX
TIDMAX = 20
endif
ifndef BARRIER
BARRIER = 0
endif
ifndef CASINLINE
CASINLINE = 0
endif
ifndef CASOFF
CASOFF = 0
endif
ifndef CASPOST
CASPOST = 0
endif
ifndef INCBARRIER
INCBARRIER = 0
endif
ifndef TID64
TID64 = 0
endif
ifndef VPTR
VPTR = 0
endif
CFLAGS += -DLOOPMAX=$(LOOPMAX)
CFLAGS += -DTIDMAX=$(TIDMAX)
CFLAGS += -D_USE_BARRIER_=$(BARRIER)
CFLAGS += -D_USE_CASINLINE_=$(CASINLINE)
CFLAGS += -D_USE_CASOFF_=$(CASOFF)
CFLAGS += -D_USE_CASPOST_=$(CASPOST)
CFLAGS += -D_USE_INCBARRIER_=$(INCBARRIER)
CFLAGS += -D_USE_TID64_=$(TID64)
CFLAGS += -D_USE_VPTR_=$(VPTR)
STDLIB += -lpthread
ALL += caslock
CLEAN += caslock
OVRPUB := 1
ifndef OVRTOP
OVRTOP := $(shell pwd)
OVRTOP := $(dir $(OVRTOP))
endif
endif
# ovrlib/rules.mk -- rules control
#
# options:
# GDB -- enable debug symbols
# 0 -- normal
# 1 -- use -O0 and define _USE_GDB_=1
#
# CLANG -- use clang instead of gcc
# 0 -- use gcc
# 1 -- use clang
#
# BNC -- enable benchmarks
# 0 -- normal mode
# 1 -- enable benchmarks for function enter/exit pairs
ifdef OVRPUB
ifndef SDIR
SDIR := $(shell pwd)
STAIL := $(notdir $(SDIR))
endif
ifndef GENTOP
GENTOP := $(dir $(SDIR))
endif
ifndef GENDIR
GENDIR := $(GENTOP)/$(STAIL)
endif
ifndef ODIR
ODIR := $(GENDIR)
endif
PROTOLST := true
PROTOGEN := #true
endif
ifndef SDIR
$(error rules: SDIR not defined)
endif
ifndef ODIR
$(error rules: ODIR not defined)
endif
ifndef GENDIR
$(error rules: GENDIR not defined)
endif
ifndef GENTOP
$(error rules: GENTOP not defined)
endif
ifndef _RULES_MK_
_RULES_MK_ = 1
CLEAN += *.proto
CLEAN += *.a
CLEAN += *.o
CLEAN += *.i
CLEAN += *.dis
CLEAN += *.TMP
QPROTO := $(shell $(PROTOLST) -i -l -O$(GENTOP) $(SDIR)/*.c $(CPROTO))
HDEP += $(QPROTO)
###VPATH += $(GENDIR)
###VPATH += $(SDIR)
ifdef INCLUDE_MK
-include $(INCLUDE_MK)
endif
ifdef GSYM
CFLAGS += -gdwarf-2
endif
ifdef GDB
CFLAGS += -gdwarf-2
DFLAGS += -D_USE_GDB_
else
CFLAGS += -O2
endif
ifndef ZPRT
DFLAGS += -D_USE_ZPRT_=0
endif
ifdef BNC
DFLAGS += -D_USE_BNC_=1
endif
ifdef CLANG
CC := clang
endif
DFLAGS += -I$(GENTOP)
DFLAGS += -I$(OVRTOP)
CFLAGS += -Wall -Werror
CFLAGS += -Wno-unknown-pragmas
CFLAGS += -Wempty-body
CFLAGS += -fno-diagnostics-color
# NOTE: we now need this to prevent inlining (enabled at -O2)
ifndef CLANG
CFLAGS += -fno-inline-small-functions
endif
# NOTE: we now need this to prevent inlining (enabled at -O3)
CFLAGS += -fno-inline-functions
CFLAGS += $(DFLAGS)
endif
all: $(PREP) proto $(ALL)
%.o: %.c $(HDEP)
$(CC) $(CFLAGS) -c -o $*.o $<
%.i: %.c
cpp $(DFLAGS) -P $*.c > $*.i
%.s: %.c
$(CC) $(CFLAGS) -S -o $*.s $<
# build a library (type (2) build)
$(LIBNAME):: $(OLIST)
ar rv $# $(OLIST)
.PHONY: proto
proto::
$(PROTOGEN) -i -v -O$(GENTOP) $(SDIR)/*.c $(CPROTO)
.PHONY: clean
clean::
rm -f $(CLEAN)
.PHONY: help
help::
egrep '^#' Makefile
caslock:: $(OLIST) $(LIBLIST) $(STDLIB)
$(CC) $(CFLAGS) -o caslock $(OLIST) $(LIBLIST) $(STDLIB)
NOTE: I may have blown some of the asm constraints because when doing the CAS function as an inline, compiling with gcc produces incorrect results. However, clang works fine with inline. So, the default is that the CAS function is not inline. For consistency, I didn't use a different default for gcc/clang, even though I could.
Here's the disassembly of the relevant function with inline as built by gcc (this fails):
00000000004009c0 <incby_tidgate>:
4009c0: 31 c0 xor %eax,%eax
4009c2: f0 0f b1 3d 3a 1a 20 lock cmpxchg %edi,0x201a3a(%rip) # 602404 <tidgate>
4009c9: 00
4009ca: 0f 94 c2 sete %dl
4009cd: 84 d2 test %dl,%dl
4009cf: 75 23 jne 4009f4 <L01>
4009d1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
4009d8:L00 64 48 8b 14 25 f8 ff mov %fs:0xfffffffffffffff8,%rdx
4009df: ff ff
4009e1: 83 42 04 01 addl $0x1,0x4(%rdx)
4009e5: f0 0f b1 3d 17 1a 20 lock cmpxchg %edi,0x201a17(%rip) # 602404 <tidgate>
4009ec: 00
4009ed: 0f 94 c2 sete %dl
4009f0: 84 d2 test %dl,%dl
4009f2: 74 e4 je 4009d8 <L00>
4009f4:L01 48 83 05 dc 17 20 00 addq $0x1,0x2017dc(%rip) # 6021d8 <ary+0xf8>
4009fb: 01
4009fc: 48 83 05 94 19 20 00 addq $0x1,0x201994(%rip) # 602398 <ary+0x2b8>
400a03: 01
400a04: c7 05 f6 19 20 00 00 movl $0x0,0x2019f6(%rip) # 602404 <tidgate>
400a0b: 00 00 00
400a0e: c3 retq
Here's the disassembly of the relevant function with inline as built by clang (this succeeds):
0000000000400990 <incby_tidgate>:
400990: 31 c0 xor %eax,%eax
400992: f0 0f b1 3d 3a 1a 20 lock cmpxchg %edi,0x201a3a(%rip) # 6023d4 <tidgate>
400999: 00
40099a: 0f 94 c0 sete %al
40099d: eb 1a jmp 4009b9 <L01>
40099f: 90 nop
4009a0:L00 64 48 8b 04 25 f8 ff mov %fs:0xfffffffffffffff8,%rax
4009a7: ff ff
4009a9: ff 40 04 incl 0x4(%rax)
4009ac: 31 c0 xor %eax,%eax
4009ae: f0 0f b1 3d 1e 1a 20 lock cmpxchg %edi,0x201a1e(%rip) # 6023d4 <tidgate>
4009b5: 00
4009b6: 0f 94 c0 sete %al
4009b9:L01 84 c0 test %al,%al
4009bb: 74 e3 je 4009a0 <L00>
4009bd: 48 ff 05 e4 17 20 00 incq 0x2017e4(%rip) # 6021a8 <ary+0xf8>
4009c4: 48 ff 05 9d 19 20 00 incq 0x20199d(%rip) # 602368 <ary+0x2b8>
4009cb: c7 05 ff 19 20 00 00 movl $0x0,0x2019ff(%rip) # 6023d4 <tidgate>
4009d2: 00 00 00
4009d5: c3 retq
4009d6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4009dd: 00 00 00
I've written a simple kernel that tries to write two characters to the frame buffer.
If I define a string literal in the kernel, I get the following output when it boots:
Booting 'os'
kernel /boot/kernel.elf
Error 13: Invalid or unsupported executable format
Press any key to continue...
Otherwise, if I define two characters I get the following (note 'ab' at the start of the output):
abBooting 'os'
kernel /boot/kernel.elf
[Multiboot-elf, <0x100000:0x201:0x0>, <0x101000:0x0:0x1000>, shtab=0x102168,
entry=0x1001f0]
loader
I wrote the loader in assembly:
global loader ; the entry symbol for ELF
MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant
FLAGS equ 0x0 ; multiboot flags
CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum
; (magic number + checksum + flags should equal 0)
KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes
section .text: ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
dd MAGIC_NUMBER ; write the magic number to the machine code,
dd FLAGS ; the flags,
dd CHECKSUM ; and the checksum
loader: ; the loader label (defined as entry point in linker script)
mov eax, 0xCAFEBABE ; place the number 0xCAFEBABE in the register eax
mov esp, kernel_stack + KERNEL_STACK_SIZE ; point esp to the start of the
; stack (end of memory area)
extern run
call run
.loop:
jmp .loop ; loop forever
section .bss
align 4 ; align at 4 bytes
kernel_stack: ; label points to beginning of memory
resb KERNEL_STACK_SIZE ; reserve stack for the kernel
The kernel is written in c
#include "io.h"
#include "fb.h"
void run()
{
// try writing message to port
char* c = (char *) 10000;
c[0] = 'a';
c[1] = 'b';
fb_write(c, 2); // this does not cause the error
// fb_write("ab",2); // this line would cause the error
}
External headers
There are two external headers. One for IO ports called io.h and one for writing to the frame buffer called fb.h
Here is io.h and the implementation io.s
io.h:
#ifndef INCLUDE_IO_H
#define INCLUDE_IO_H
/** outb:
* Sends the given data to the given I/O port. Defined in io.s
*
* #param port The I/O port to send the data to
* #param data The data to send to the I/O port
*/
void outb(unsigned short port, unsigned char data);
#endif /* INCLUDE_IO_H */
io.s:
global outb ; make the label outb visible outside this file
; outb - send a byte to an I/O port
; stack: [esp + 8] the data byte
; [esp + 4] the I/O port
; [esp ] return address
outb:
mov al, [esp + 8]
mov dx, [esp + 4]
out dx, al
ret
fb.h
#include "io.h"
// FRAME BUFFER ================================
// Text colors
#define FB_BLACK 0
#define FB_BLUE 1
#define FB_GREEN 2
#define FB_CYAN 3
#define FB_RED 4
#define FB_MAGENTA 5
#define FB_BROWN 6
#define FB_LT_GREY 7
#define FB_DARK_GREY 8
#define FB_LT_BLUE 9
#define FB_LT_GREEN 10
#define FB_LT_CYAN 11
#define FB_LT_RED 12
#define FB_LT_MAGENTA 13
#define FB_LT_BROWN 14
#define FB_WHITE 15
// IO PORTS
#define FB_COMMAND_PORT 0x3D4
#define FB_DATA_PORT 0x3D5
// IO PORT COMMANDS
#define FB_HIGH_BYTE_COMMAND 14 // move cursor command low
#define FB_LOW_BYTE_COMMAND 15 // move cursor command high
/** fb_write_cell:
* used to write a character to a cell in the framebuffer
*
* param i which cell to write to
* param c the ascii char to write
* param fg foreground color
* param bf background color
*/
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg);
/** fb_move_cursor:
* used to move the cursor within the frame buffer
*
* param pos position within frame buffer to move cursor to
*/
void fb_move_cursor(unsigned short pos);
/** fb_write:
* write some text to the cursor
*
* param buf pointer to character string
* param len length of string to write
*/
int fb_write(char *buf, unsigned int len);
fb.c
#include "fb.h"
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg)
{
char *fb = (char *) 0x000B8000;
fb[i*2] = c;
fb[i*2 + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F);
}
void fb_move_cursor(unsigned short pos) {
outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND);
outb(FB_DATA_PORT, ((pos>>8) & 0x00FF));
outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND);
outb(FB_DATA_PORT, pos & 0x00FF);
}
int fb_write(char *buf, unsigned int len) {
unsigned int i = 0;
for(i = 0; i < len; i++) {
fb_write_cell(i, buf[i], FB_BLACK, FB_WHITE);
}
return 0;
}
Building it
I have a linker script called link.ld and a Makefile. I'm using gcc cross compiler for i386-elf That I compiled using this guide (http://wiki.osdev.org/GCC_Cross-Compiler).
ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
sbss = .;
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
ebss = .;
}
}
And here is my makefile
OBJECTS = io.o fb.o loader.o kmain.o
#CC = gcc
CC = /home/albertlockett/opt/cross/bin/i386-elf-gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $#
%.o: %.s
$(AS) $(ASFLAGS) $< -o $#
clean:
rm -rf *.o kernel.elf os.iso
Run it
The makefile builds an iso from the contents of a directory called iso. That folder contains a preconfigured version of grub that I got here (https://github.com/littleosbook/littleosbook/blob/master/files/stage2_eltorito) and a menu.lst file for grub
menu.lst:
default=0
timeout=0
title os
kernel /boot/kernel.elf
contents of iso directory:
iso
`-- boot
|-- grub
| |-- menu.lst
| `-- stage2_eltorito
`-- kernel.elf
The iso image boots in bochs. Here is my bochsrc.txt file
megs: 32
display_library: term
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000
com1: enabled=1, mode=file, dev=com1.out
Does anyone know why the string literal in the kernel file produces the error when I try to boot the iso?
You have an extra colon at the end of section .text: so that creates a new section named .text:. For some obscure reason that I couldn't find out from a quick glance at the documentation, this section is emitted to the output even though it is not listed in your linker script. When you have no literal data in the C code, you are lucky that it still falls within the first 8kiB of the image, so that the multiboot header is in the required portion. If you do have a string literal, you will get a new section .rodata and that, for yet another obscure reason, gets sorted before your .text: but after the standard .text. Example:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000001 00100000 00100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata 00000005 00101000 00101000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .text: 00000018 00101008 00101008 00002008 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .bss 0000100a 00102000 00102000 00003000 2**2
ALLOC
As you can see it's no longer within the first 8kiB of the image, so grub will be very sad.
TL;DR: remove the extra colon after section .text:.
I am using an ARM M3 based MCU, CC2538. I use Code Composer Studio v6 as an IDE. When I select GNU v4.7.4 (Linaro) as a compiler, I get a warning message that " selected device does not currently provide project initialization details to the GNU toolchain". I assume it is due to setup.c(?) that will be added later on.
the compiler has the following flags
`"-mcpu=cortex-m3 -mthumb -mfpu=vfp -I"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/arm-none-eabi/include" -g -gstrict-dwarf -Wall -mno -thumb-interwork -msoft-float -mfix-cortex-m3-ldrd"
and linker flags: -mthumb -g -gstrict-dwarf -Wall -Wl,-Map,"cc2328em_test.map"
I use the source files provided by TI http://www.ti.com/lit/zip/swra443
I add main.c, setup.c, setup_gcc.c, and cc2538_512.cmd into an empty project, then compile it. here is the compile output:
**** Build of configuration Debug for project cc2328em_test ****
"c:\\ti\\ccsv6\\utils\\bin\\gmake" -k all
'Building file: ../main.c'
'Invoking: GNU Compiler'
"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-gcc.exe" -c -mcpu=cortex-m3 -mthumb -mfpu=vfp -I"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/arm-none-eabi/include" -ffunction-sections -fdata-sections -g -gstrict-dwarf -Wall -MMD -MP -MF"main.d" -MT"main.d" -mno-thumb-interwork -msoft-float -mfix-cortex-m3-ldrd -fshort-enums -fomit-frame-pointer -fno-strict-aliasing -o"main.o" "../main.c"
'Finished building: ../main.c'
' '
'Building file: ../setup.c'
'Invoking: GNU Compiler'
"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-gcc.exe" -c -mcpu=cortex-m3 -mthumb -mfpu=vfp -I"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/arm-none-eabi/include" -ffunction-sections -fdata-sections -g -gstrict-dwarf -Wall -MMD -MP -MF"setup.d" -MT"setup.d" -mno-thumb-interwork -msoft-float -mfix-cortex-m3-ldrd -fshort-enums -fomit-frame-pointer -fno-strict-aliasing -o"setup.o" "../setup.c"
'Finished building: ../setup.c'
' '
'Building file: ../startup_gcc.c'
'Invoking: GNU Compiler'
"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-gcc.exe" -c -mcpu=cortex-m3 -mthumb -mfpu=vfp -I"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/arm-none-eabi/include" -ffunction-sections -fdata-sections -g -gstrict-dwarf -Wall -MMD -MP -MF"startup_gcc.d" -MT"startup_gcc.d" -mno-thumb-interwork -msoft-float -mfix-cortex-m3-ldrd -fshort-enums -fomit-frame-pointer -fno-strict-aliasing -o"startup_gcc.o" "../startup_gcc.c"
'Finished building: ../startup_gcc.c'
' '
'Building target: cc2328em_test.out'
'Invoking: GNU Linker'
"c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-gcc.exe" -mthumb -ffunction-sections -fdata-sections -g -gstrict-dwarf -Wall -Wl,-Map,"cc2328em_test.map" -nostartfiles -mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections -g -gstrict-dwarf -Wall -o"cc2328em_test.out" "./main.o" "./setup.o" "./startup_gcc.o" "../cc2538_512_gcc.cmd" -Wl,--start-group -l"c" -l"gcc" -l"nosys" -Wl,--end-group
./startup_gcc.o:(.flashcca+0x0): multiple definition of `__cca'
./setup.o:(.flashcca+0x0): first defined here
c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/bin/ld.exe: warning: ../cc2538_512_gcc.cmd contains output sections; did you forget -T?
c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/bin/ld.exe: cc2328em_test.out section `.flashcca' will not fit in region `FLASH_CCA'
c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/bin/ld.exe: region `FLASH_CCA' overflowed by 12 bytes
c:/ti/ccsv6/tools/compiler/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 00200000
collect2.exe: error: ld returned 1 exit status
gmake: *** [cc2328em_test.out] Error 1
gmake: Target `all' not remade because of errors.
**** Build Finished ****
here is the first error "./startup_gcc.o:(.flashcca+0x0): multiple definition of `__cca'"
Does the source code have a mistake? Do I use a wrong toolchain (I downloaded the GNU tool chain from TI's APP center as "ARM GCC" with GCC logo. or do I miss any configuration?
Here are the source files that TI supplies:
main.c
#define GPIO_C_DIR 0x400DB400
#define GPIO_C_DATA 0x400DB000
#define HWREG(x) \
(*((volatile unsigned long *)(x)))
int main(void)
{
volatile unsigned long ulLoopCount;
// Set direction output and initial value for PC2 and PC0
// Greed LED on PC2
// Red LED on PC0
HWREG(GPIO_C_DIR) |= 0x05;
HWREG(GPIO_C_DATA + (0x05 << 2)) = 0;
// Loop forever.
while(1)
{
// Turn on both LED's.
HWREG(GPIO_C_DATA + (0x05 << 2)) ^= 0x05;
// Delay for a bit
for(ulLoopCount = 200000; ulLoopCount > 0; ulLoopCount--)
{
}
}
return 0;
}
setup.c
#include <stdint.h>
#define FLASH_START_ADDR 0x00200000
#define BOOTLOADER_BACKDOOR_DISABLE 0xEFFFFFFF
#define SYS_CTRL_EMUOVR 0x400D20B4
#define SYS_CTRL_I_MAP 0x400D2098
//*****************************************************************************
//
// Customer Configuration Area in Lock Page
// Holds Image Vector table address (bytes 2013 - 2015) and
// Image Valid bytes (bytes 2008 -2011)
//
//*****************************************************************************
typedef struct
{
uint32_t ui32BootldrCfg;
uint32_t ui32ImageValid;
uint32_t ui32ImageVectorAddr;
}
lockPageCCA_t;
#ifdef __IAR_SYSTEMS_ICC__
__root const lockPageCCA_t __cca # ".flashcca" =
#elif __TI_COMPILER_VERSION__
#pragma DATA_SECTION(__cca, ".flashcca")
#pragma RETAIN(__cca)
const lockPageCCA_t __cca =
#else
__attribute__ ((section(".flashcca"), used))
const lockPageCCA_t __cca =
#endif
{
BOOTLOADER_BACKDOOR_DISABLE, // Bootloader backdoor disabled
0, // Image valid bytes
FLASH_START_ADDR // Vector table located at flash start address
};
startup_gcc.c
#include "setup.c"
//*****************************************************************************
//
// Macro for hardware access, both direct and via the bit-band region.
//
//*****************************************************************************
#ifndef HWREG
#define HWREG(x) \
(*((volatile unsigned long *)(x)))
#endif
extern int main (void);
void ResetISR(void);
void NmiSR(void);
void FaultISR(void);
void IntDefaultHandler(void);
//*****************************************************************************
//
// Reserve space for the system stack.
//
//*****************************************************************************
static uint32_t pui32Stack[128];
__attribute__ ((section(".vectors"), used))
void (* const gVectors[])(void) =
{
(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)), // Stack pointer
ResetISR, // Reset handler
NmiSR, // The NMI handler
FaultISR, // The hard fault handler
IntDefaultHandler, // 4 The MPU fault handler
IntDefaultHandler, // 5 The bus fault handler
IntDefaultHandler, // 6 The usage fault handler
0, // 7 Reserved
0, // 8 Reserved
0, // 9 Reserved
0, // 10 Reserved
IntDefaultHandler, // 11 SVCall handler
IntDefaultHandler, // 12 Debug monitor handler
0, // 13 Reserved
IntDefaultHandler, // 14 The PendSV handler
IntDefaultHandler, // 15 The SysTick handler
IntDefaultHandler, // 16 GPIO Port A
IntDefaultHandler, // 17 GPIO Port B
IntDefaultHandler, // 18 GPIO Port C
IntDefaultHandler, // 19 GPIO Port D
0, // 20 none
IntDefaultHandler, // 21 UART0 Rx and Tx
IntDefaultHandler, // 22 UART1 Rx and Tx
IntDefaultHandler, // 23 SSI0 Rx and Tx
IntDefaultHandler, // 24 I2C Master and Slave
0, // 25 Reserved
0, // 26 Reserved
0, // 27 Reserved
0, // 28 Reserved
0, // 29 Reserved
IntDefaultHandler, // 30 ADC Sequence 0
0, // 31 Reserved
0, // 32 Reserved
0, // 33 Reserved
IntDefaultHandler, // 34 Watchdog timer, timer 0
IntDefaultHandler, // 35 Timer 0 subtimer A
IntDefaultHandler, // 36 Timer 0 subtimer B
IntDefaultHandler, // 37 Timer 1 subtimer A
IntDefaultHandler, // 38 Timer 1 subtimer B
IntDefaultHandler, // 39 Timer 2 subtimer A
IntDefaultHandler, // 40 Timer 2 subtimer B
IntDefaultHandler, // 41 Analog Comparator 0
IntDefaultHandler, // 42 RFCore Rx/Tx
IntDefaultHandler, // 43 RFCore Error
IntDefaultHandler, // 44 IcePick
IntDefaultHandler, // 45 FLASH Control
IntDefaultHandler, // 46 AES
IntDefaultHandler, // 47 PKA
IntDefaultHandler, // 48 Sleep Timer
IntDefaultHandler, // 49 MacTimer
IntDefaultHandler, // 50 SSI1 Rx and Tx
IntDefaultHandler, // 51 Timer 3 subtimer A
IntDefaultHandler, // 52 Timer 3 subtimer B
0, // 53 Reserved
0, // 54 Reserved
0, // 55 Reserved
0, // 56 Reserved
0, // 57 Reserved
0, // 58 Reserved
0, // 59 Reserved
IntDefaultHandler, // 60 USB 2538
0, // 61 Reserved
IntDefaultHandler, // 62 uDMA
IntDefaultHandler, // 63 uDMA Error
#ifndef CC2538_USE_ALTERNATE_INTERRUPT_MAP
0, // 64 64-155 are not in use
0, // 65
0, // 66
0, // 67
0, // 68
0, // 69
0, // 70
0, // 71
0, // 72
0, // 73
0, // 74
0, // 75
0, // 76
0, // 77
0, // 78
0, // 79
0, // 80
0, // 81
0, // 82
0, // 83
0, // 84
0, // 85
0, // 86
0, // 87
0, // 88
0, // 89
0, // 90
0, // 91
0, // 92
0, // 93
0, // 94
0, // 95
0, // 96
0, // 97
0, // 98
0, // 99
0, // 100
0, // 101
0, // 102
0, // 103
0, // 104
0, // 105
0, // 106
0, // 107
0, // 108
0, // 109
0, // 110
0, // 111
0, // 112
0, // 113
0, // 114
0, // 115
0, // 116
0, // 117
0, // 118
0, // 119
0, // 120
0, // 121
0, // 122
0, // 123
0, // 124
0, // 125
0, // 126
0, // 127
0, // 128
0, // 129
0, // 130
0, // 131
0, // 132
0, // 133
0, // 134
0, // 135
0, // 136
0, // 137
0, // 138
0, // 139
0, // 140
0, // 141
0, // 142
0, // 143
0, // 144
0, // 145
0, // 146
0, // 147
0, // 148
0, // 149
0, // 150
0, // 151
0, // 152
0, // 153
0, // 154
0, // 155
IntDefaultHandler, // 156 USB
IntDefaultHandler, // 157 RFCORE RX/TX
IntDefaultHandler, // 158 RFCORE Error
IntDefaultHandler, // 159 AES
IntDefaultHandler, // 160 PKA
IntDefaultHandler, // 161 SMTimer
IntDefaultHandler, // 162 MACTimer
#endif
};
//*****************************************************************************
//
// The following are constructs created by the linker, indicating where the
// the "data" and "bss" segments reside in memory. The initializers for the
// for the "data" segment resides immediately following the "text" segment.
//
//*****************************************************************************
extern uint32_t _etext;
extern uint32_t _data;
extern uint32_t _edata;
extern uint32_t _bss;
extern uint32_t _ebss;
//
// And here are the weak interrupt handlers.
//
void
NmiSR (void)
{
ResetISR();
while(1)
{
}
}
void
FaultISR (void)
{
while(1)
{
}
}
void
IntDefaultHandler (void)
{
while(1)
{
}
}
void
ResetISR (void)
{
uint32_t *pui32Src, *pui32Dest;
//
// Workaround for PM debug issue
//
HWREG(SYS_CTRL_EMUOVR) = 0xFF;
//
// Copy the data segment initializers from flash to SRAM.
//
pui32Src = &_etext;
for(pui32Dest = &_data; pui32Dest < &_edata; )
{
*pui32Dest++ = *pui32Src++;
}
//
// Zero fill the bss segment.
//
__asm(" ldr r0, =_bss\n"
" ldr r1, =_ebss\n"
" mov r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
" cmp r0, r1\n"
" it lt\n"
" strlt r2, [r0], #4\n"
" blt zero_loop");
#ifdef CC2538_USE_ALTERNATE_INTERRUPT_MAP
//
// Enable alternate interrupt mapping
//
HWREG(SYS_CTRL_I_MAP) |= 1;
#endif
//
// Call the application's entry point.
//
main();
//
// End here if return from main()
//
while(1)
{
}
}
cc2538_512_gcc.cmd
/******************************************************************************
*
*
* cc2538_512_gcc.cmd - Example GCC linker configuration file for CC2538
*
*
*****************************************************************************/
MEMORY
{
FLASH (rx) : ORIGIN = 0x200000, LENGTH = 0x0007FFD4
FLASH_CCA (RX) : ORIGIN = 0x0027FFD4, LENGTH = 12
SRAM_NON_RETENTION (RWX) : ORIGIN = 0x20000000, LENGTH = 0x00004000
SRAM_RETENTION (RWX) : ORIGIN = 0x20004000, LENGTH = 0x00004000
}
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.vectors))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH= 0
.data : AT(ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM_NON_RETENTION
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM_NON_RETENTION
.flashcca :
{
} > FLASH_CCA
}
Here is a printscreen that shows the configurations and the related files on the left side menu
The first line of startup_gcc.c is #include "setup.c"; do you see a problem? Don't build setup.c separately if you do this.
Reading the error message multiple definition of '__cca' will help; computers rarely lie. You can compile this twice. Also, your .flashcca output section in the linker script has no input files and this should be fixed.
For example,
.flashcca :{*(.flashcca);} > FLASH_CCA
Without *(.flashcca);, the .flashcca :{} > FLASH_CCA is useless. The first .flashcca is an output section (ie. some place in final binary). The stuff inside the brackets is an input section (ie, your source/object). A major choir of the linker is to take multiple input sections and place them in an output section. The other job is to revolve external symbols; it can not do this when it has two of the same name provided; there is no way to know which on to use. This is why you have multiple definition of '__cca'.