code:
# Generated by Django 2.1.7 on 2019-04-08 23:31

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0013_auto_20190408_2330'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 31, 4, 158967, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(blank=True, default=False),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 31, 4, 159876, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-08 23:32

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0014_auto_20190408_2331'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 32, 3, 966968, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(default=False, null=True),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 32, 3, 967834, tzinfo=utc)),
        ),
    ]

repo_name: jazzyeagle/ccradio

license: unknown


code:
# Generated by Django 2.1.7 on 2019-04-08 23:35

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0015_auto_20190408_2332'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 35, 15, 142937, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(default=False),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 35, 15, 143837, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-09 00:01

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0016_auto_20190408_2335'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 0, 1, 40, 294438, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(default=False),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 0, 1, 40, 295317, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-09 00:20

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0017_auto_20190409_0001'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 0, 20, 35, 313062, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(blank=True),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 0, 20, 35, 313993, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-09 12:57

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0018_auto_20190409_0020'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 12, 57, 34, 204879, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(default=False),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 9, 12, 57, 34, 205813, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-10 01:34

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0019_auto_20190409_1257'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 10, 1, 34, 28, 83874, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 10, 1, 34, 28, 84740, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-10 02:39

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0020_auto_20190410_0134'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='song',
            name='nsfw',
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 10, 2, 39, 22, 277812, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 10, 2, 39, 22, 278683, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-15 23:45

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0021_auto_20190410_0239'),
    ]

    operations = [
        migrations.AddField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(default=False),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 15, 23, 45, 32, 958249, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 15, 23, 45, 32, 959219, tzinfo=utc)),
        ),
    ]


code:
from django.db import models
from django.utils.timezone import now
from django.urls import reverse_lazy
from users.models import CCUser

def artist_path(instance, filename):
    return 'music/{0}/{1}'.format(instance.artist.handle, filename)

def album_path(instance, filename):
    return 'music/{0}/{1}/{2}'.format(instance.album.artist.handle, instance.album.title, filename)

def song_path(instance, filename):
    return 'music/{0}/{1}/{2}'.format(instance.artist.handle, instance.album.title, filename)

# Create your models here.
class Artist(models.Model):
    handle = models.SlugField(max_length=50, primary_key=True)
    name = models.CharField(max_length=200)
    bio = models.CharField(max_length=1000, null=True, blank=True)
    members = models.ManyToManyField(CCUser,
                                     through='BandMember',
                                     through_fields=('artist', 'member'),
                                    )
    logo = models.ImageField(upload_to=artist_path, null=True, blank=True)
    created_date = models.DateField(auto_now_add=True)
    modified_date = models.DateField(auto_now=True)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse_lazy('artist', kwargs={'artist_handle': self.handle})

class Role(models.Model):
    role = models.CharField(max_length=30, primary_key=True)

    def __str__(self):
        return self.role

class BandMember(models.Model):
    member = models.ForeignKey(CCUser, on_delete=models.CASCADE)
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
    #role = models.ForeignKey(Role, on_delete=models.CASCADE)

class License(models.Model):
    abbreviation = models.CharField(max_length=20, primary_key=True)
    name = models.CharField(max_length=100)
    version = models.DecimalField(max_digits=3, decimal_places = 1)
    large_image = models.URLField()
    small_image = models.URLField()
    legaleze = models.URLField()

    def __str__(self):
        return self.abbreviation

class Album(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200)
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
    cover = models.ImageField(upload_to=album_path, null=True, blank=True)
    license = models.ForeignKey(License, on_delete=models.SET_NULL, null=True)
    release_date = models.DateField(default=now())
    created_date = models.DateField(auto_now_add=True)
    modified_date = models.DateField(auto_now=True)

    def __str__(self):
        return self.artist.name + ' - ' + self.title

class Song(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200)
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
    album = models.ForeignKey(Album, on_delete=models.CASCADE)
    license = models.ForeignKey(License, on_delete=models.SET_NULL, null=True)
    nsfw = models.BooleanField(default=False)
    file = models.FileField(upload_to=song_path)
    release_date = models.DateField(default=now())
    created_date = models.DateField(auto_now_add=True)
    modified_date = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.artist.name + ' - ' + self.title + ' (' + self.album.title + ')'


code:
* {
    box-sizing: border-box;
}

.col-1  { width: 8.33%  }
.col-2  { width: 16.66% }
.col-3  { width: 25%    }
.col-4  { width: 33.33% }
.col-5  { width: 41.66% }
.col-6  { width: 50%    }
.col-7  { width: 58.33% }
.col-8  { width: 66.66% }
.col-9  { width: 75%    }
.col-10 { width: 83.33% }
.col-11 { width: 91.66% }
.col-12 { width: 100%   }

[ class*="col-"] {
    float: left;
    padding: 15px;
}

.row::after {
    content: "";
    clear: both;
    display: table;
}


code:
.ccnav {
    position: fixed;
    overflow: hidden;
    top: 0;
    padding-top: 0%;
    height: 10%;
    width: 100%;
    border: none;
}

.ccpage {
    position: fixed;
    overflow: hidden;
    top: 10%;
    padding-top: 0%;
    height: 90%;
    width: 100%;
    border: none;
}

.cciframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}


code:
.ccnavbar {
    background-color: DarkOrange;
    overflow: hidden;
}

.ccnavbar-item {
    display: inline-block;
}

.ccnavbar a {
    color: #F2F2F2;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
    font-size: 17px;
}

.ccnavbar a:hover {
    background-color: Gold;
    color: Black;
}

.active {
    background-color: Yellow;
    color: White;
}

.ccnavbar .icon {
    display: none;
}


code:
from django.urls import path
# from django.conf.urls import url
from . import views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('artists', views.ArtistsView.as_view(), name='artists'),
    path('navbar', views.NavbarView.as_view(), name='navbar'),
    path('welcome', views.WelcomeView.as_view(), name='welcome'),

    path('newartist', views.ArtistCreate.as_view(), name='artist_create'),
    path('<artist_handle>', views.ArtistView.as_view(), name='artist'),
    path('<artist_handle>/edit', views.ArtistUpdate.as_view(), name='artist_update'),
    path('<artist_handle>/delete', views.ArtistDelete.as_view(), name='artist_delete'),

    path('<artist_handle>/newalbum', views.AlbumCreate.as_view(), name='album_create'),
    path('<artist_handle>/<album_slug>', views.AlbumView.as_view(), name='album'),
    path('<artist_handle>/<album_slug>/addsong', views.SongAdd.as_view(), name='song_add')
]


code:
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.views.generic import DetailView, ListView, TemplateView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from .models import Artist, Album, Song, BandMember

class ArtistsView(ListView):
    template_name = 'website/artists.html'
    context_object_name = 'artists'

    def get_queryset(self):
        return Artist.objects.all()

class ArtistView(DetailView):
    model = Artist
    template_name = 'website/artist.html'
    pk_url_kwarg = 'artist_handle'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['albums'] = Album.objects.filter(artist=self.kwargs['artist_handle'])
        if(self.request.user.is_authenticated):
            context['member'] = BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            context['member'] = False
        return context

class ArtistCreate(LoginRequiredMixin, CreateView):
    model = Artist
    fields = ['name', 'handle']
    template_name = 'website/artist_create.html'

    def form_valid(self, form):
        a = form.save()
        BandMember.objects.create(member=self.request.user, artist=a)
        return HttpResponseRedirect(reverse('artist', kwargs={'artist_handle': a.handle}))

class ArtistUpdate(UserPassesTestMixin, UpdateView):
    model = Artist
    fields = ['name', 'bio', 'logo']
    template_name = 'website/artist_update.html'
    pk_url_kwarg = 'artist_handle'
    raise_exception = True

    # Only allow band members to edit the Artist page
    def test_func(self):
        if self.request.user.is_authenticated:
            return BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            return False

class ArtistDelete(UserPassesTestMixin, DeleteView):
    model = Artist
    tempate_name = 'website/artist_confirm_delete.html'
    pk_url_kwarg = 'artist_handle'
    raise_exception = True
    success_url = '/artists'

    # Only allow band members to edit the Artist page
    def test_func(self):
        if self.request.user.is_authenticated:
            return BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            return False

class AlbumView(DetailView):
    model = Album
    template_name = 'website/album.html'

    def get_queryset(self):
        self.artist = get_object_or_404(Artist, handle=self.kwargs['artist_handle'])
        return Album.objects.filter(handle=artist_handle)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['songs'] = Song.objects.filter(artist=self.kwargs['artist_handle'], album__slug__contains=self.kwargs['album_slug'])
        if(self.request.user.is_authenticated):
            context['member'] = BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            context['member'] = False
        return context

    def get_object(self):
        return get_object_or_404(Album, slug=self.kwargs['album_slug'])

class AlbumCreate(UserPassesTestMixin, CreateView):
    model = Album
    fields = ['title', 'cover', 'license', 'release_date']
    template_name = 'website/album_create.html'

    # Only allow band members to create/edit/delete album pages
    def test_func(self):
        if self.request.user.is_authenticated:
            return BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            return False

    def form_valid(self, form):
        a = form.save(commit=False)
        a.artist = get_object_or_404(Artist, handle=self.kwargs['artist_handle'])
        a.slug = slugify(a.title)
        a.save()
        return HttpResponseRedirect(reverse('album', kwargs={'artist_handle': a.artist.handle, 'album_slug': a.slug}))

class SongAdd(UserPassesTestMixin, CreateView):
    model = Song
    fields = ['title', 'license', 'nsfw', 'file']
    template_name = 'website/song_add.html'

    # Only allow band members to create/edit/delete album pages
    def test_func(self):
        if self.request.user.is_authenticated:
            return BandMember.objects.filter(artist=self.kwargs['artist_handle'], member=self.request.user).exists()
        else:
            return False

    def form_valid(self, form):
        a = form.save(commit=False)
        a.artist = get_object_or_404(Artist, handle=self.kwargs['artist_handle'])
        a.album = get_object_or_404(Album, slug=self.kwargs['album_slug'])
        a.slug = slugify(a.title)
        a.release_date = a.album.release_date
        a.save()
        return HttpResponseRedirect(reverse('album', kwargs={'artist_handle': a.artist.handle, 'album_slug': a.album.slug}))

class IndexView(TemplateView):
    template_name = 'website/index.html'

class NavbarView(TemplateView):
    template_name = 'website/navbar.html'

class WelcomeView(TemplateView):
    template_name = 'website/welcome.html'


code:
    # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
    # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

    # User-specific stuff
    .idea/**/workspace.xml
    .idea/**/tasks.xml
    .idea/**/usage.statistics.xml
    .idea/**/dictionaries
    .idea/**/shelf

    # AWS User-specific
    .idea/**/aws.xml

    # Generated files
    .idea/**/contentModel.xml

    # Sensitive or high-churn files
    .idea/**/dataSources/
    .idea/**/dataSources.ids
    .idea/**/dataSources.local.xml
    .idea/**/sqlDataSources.xml
    .idea/**/dynamic.xml
    .idea/**/uiDesigner.xml
    .idea/**/dbnavigator.xml

    # Gradle
    .idea/**/gradle.xml
    .idea/**/libraries

    # Gradle and Maven with auto-import
    # When using Gradle or Maven with auto-import, you should exclude module files,
    # since they will be recreated, and may cause churn.  Uncomment if using
    # auto-import.
    # .idea/artifacts
    # .idea/compiler.xml
    # .idea/jarRepositories.xml
    # .idea/modules.xml
    # .idea/*.iml
    # .idea/modules
    # *.iml
    # *.ipr

    # CMake
    cmake-build-*/

    # Mongo Explorer plugin
    .idea/**/mongoSettings.xml

    # File-based project format
    *.iws

    # IntelliJ
    out/

    # mpeltonen/sbt-idea plugin
    .idea_modules/

    # JIRA plugin
    atlassian-ide-plugin.xml

    # Cursive Clojure plugin
    .idea/replstate.xml

    # Crashlytics plugin (for Android Studio and IntelliJ)
    com_crashlytics_export_strings.xml
    crashlytics.properties
    crashlytics-build.properties
    fabric.properties

    # Editor-based Rest Client
    .idea/httpRequests

    # Android studio 3.1+ serialized cache file
    .idea/caches/build_file_checksums.ser


code:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/out" />
  </component>
</project>


code:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/Java3Level.iml" filepath="$PROJECT_DIR$/Java3Level.iml" />
    </modules>
  </component>
</project>


code:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RunConfigurationProducerService">
    <option name="ignoredProducers">
      <set>
        <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
      </set>
    </option>
  </component>
</project>


code:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>


code:
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>


code:
Java level 3 on GeekBrains.
Lest do this amazing.
Homework branch.


code:
define x15_connect_qemu
    target remote localhost:1234
end

path: .gdbinit

license: mit


code:
.*
*.o
*.d
tags
cscope.out
x15.lds
/include/generated
/x15
/doc/*\.[1-9]
/doc/*\.[1-9]\.html
/doc/*\.[1-9]\.xml

path: .gitignore


code:
#
# For a description of the syntax of this configuration file,
# see doc/kbuild/kconfig-language.txt.
#
mainmenu "X15/$ARCH $VERSION Kernel Configuration"

config ARCH
	string
	option env="ARCH"

config VERSION
	string
	option env="VERSION"

config KERNEL_VERSION
	string
	default VERSION

config CC
	string
	option env="CC"

config CFLAGS
	string
	option env="CFLAGS"

menu "Build options"

config COMPILER
	string "Compiler executable"
	default CC
	---help---
	  Name of the compiler executable

	  This includes any toolchain prefix, and must be a file name,
	  not a path.

config COMPILER_OPTIONS
	string "Compilation options"
	default CFLAGS
	---help---
	  Raw options passed to the compiler.

config ASSERT
	bool "Assertions"
	default y
	---help---
	  Enable assert() code generation.

config SYMTAB
	bool "Build symbol table"
	default y
	---help---
	  Build and embed a symbol table in the kernel.

	  This option allows the kernel to perform symbolic address
	  resolutions which are very convenient for debugging, at the
	  cost of additional data memory.

	  If unsure, enable.

endmenu

source "arch/$ARCH/Kconfig"
source "kern/Kconfig"
source "test/Kconfig"

path: Kconfig


code:
SHELL := /bin/sh

MAKEFLAGS += -rR
MAKEFLAGS += --no-print-directory

.PHONY: all
all: x15 docs

VERSION = 0.1
export VERSION

COMMA := ,

ifndef V
V := 0
endif

# Use callable variables so that commands can be split into multiple
# lines, but produce a single line when echoed. This makes copying
# commands easy. It also makes them somewhat self-describing.

ifeq ($(V),0)
Q := @

# $(call xbuild_action_print,<action_short_name>,<target>)
define xbuild_action_print
@printf "  %-7s %s\n" $(1) $(2)
@
endef
else ifneq ($(V),1)
$(error invalid value for V)
endif

export Q

# $(call xbuild_action_mkdir,<target>)
define xbuild_action_mkdir
	$(Q)mkdir -p $(dir $(1))
endef

# $(call xbuild_action,<action>,<target>)
define xbuild_action
	$(call xbuild_action_mkdir,$(2))
	$(call xbuild_action_print,$(1),$(2))
endef

define xbuild_kconfig_invoke
	$(Q)$(MAKE) -f $(SRCDIR)/$(KCONFIG_PATH)/Makefile $@
endef

define xbuild_gen_autoconf_h
	$(call xbuild_action,GEN,$@)cat $< \
		| sed -e 's/^\([^#]\)/#define \1/g' \
		      -e 's/=/ /' \
		| grep '^#define' > $@
endef

# $(call xbuild_check_cc_option,<option>)
define xbuild_check_cc_option
$(shell printf "int main(void){ return 0; }\n" \
        | $(CC) -Wall -Werror -x c $(1) -c - -o /dev/null 2> /dev/null \
        && printf -- "%s" $(1))
endef

# $(call xbuild_replace_source_suffix,<suffix>,<file_names>)
define xbuild_replace_source_suffix
$(sort $(patsubst %.c,%.$(1),$(filter %.c,$(2))) \
       $(patsubst %.S,%.$(1),$(filter %.S,$(2))))
endef

define xbuild_compile
	$(call xbuild_action,CC,$@) \
		$(COMPILE) -MMD -MP -c -o $@ $<
endef

define xbuild_gen_linker_script
	$(call xbuild_action,LDS,$@) \
		$(CPP) $(XBUILD_CPPFLAGS) -MMD -MP \
		-MF $@.d -MT $@ -P -o $@ $<
endef

# $(call xbuild_link,<objects>)
define xbuild_link
	$(call xbuild_action,LD,$@) \
		$(COMPILE) -o $@ $(1) $(XBUILD_LDFLAGS)
endef

XBUILD_GEN_SYMTAB = $(SRCDIR)/tools/gen_symtab.py
XBUILD_GEN_SYMTAB_DEPS = $(XBUILD_GEN_SYMTAB)

# $(call xbuild_gen_symtab)
define xbuild_gen_symtab
	$(call xbuild_action,GEN,$@) \
		$(NM) -S -n $< | $(XBUILD_GEN_SYMTAB) > $@
endef

define xbuild_clean
	$(Q)rm -f x15 $(x15_NO_SYMTAB) \
	$(x15_OBJDEPS) $(x15_OBJECTS) \
	$(x15_SYMTAB_C) $(x15_SYMTAB_D) $(x15_SYMTAB_O) \
	$(x15_LDS_D) $(x15_LDS)
endef

define xbuild_distclean
	$(clean)
	$(Q)rm -f .config \
	.config.old \
	include/generated/autoconf.h \
	include/generated/autoconf.mk
endef

ARCH ?= $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ )
export ARCH

SRCDIR := $(realpath $(dir $(realpath $(firstword $(MAKEFILE_LIST)))))
VPATH := $(SRCDIR)
export SRCDIR VPATH

PREFIX ?= /usr/local
DATAROOTDIR ?= share

# Do not use MAKEFILE_LIST as its value is updated only on inclusion,
# which makes it unsuitable as a rule dependency in most of the file.
#
# These additional Makefiles are included in order.
MAKEFILE_INCLUDES := \
        $(SRCDIR)/arch/$(ARCH)/Makefile \
        $(SRCDIR)/doc/Makefile \
        $(SRCDIR)/kern/Makefile \
        $(SRCDIR)/test/Makefile \
        $(SRCDIR)/vm/Makefile
ALL_MAKEFILES := $(MAKEFILE_LIST) $(MAKEFILE_INCLUDES)

ifeq ($(words $(MAKECMDGOALS)),0)
else ifeq ($(words $(MAKECMDGOALS)),1)
else
$(error up to one target may be given)
endif

KCONFIG_PATH := tools/kconfig

# Export to Kconfig
export KCONFIG_PATH

HOSTCC := $(if $(shell type gcc 2>/dev/null),gcc,cc)
HOSTCXX = g++
HOSTCFLAGS := -g
HOSTCXXFLAGS := -g

# Export to Kconfig
export HOSTCC HOSTCXX HOSTCFLAGS HOSTCXXFLAGS

BOARDS := $(wildcard $(SRCDIR)/arch/$(ARCH)/configs/*_defconfig)
BOARDS := $(sort $(notdir $(BOARDS)))

.PHONY: help
help:
	@printf 'Configuration targets:\n'
	@$(Q)$(MAKE) -f $(SRCDIR)/$(KCONFIG_PATH)/Makefile $@
	@printf '\n'
	@printf 'Cleaning targets:\n'
	@printf '  clean                    - Remove most generated files but keep configuration\n'
	@printf '  distclean                - Remove all generated files\n'
	@printf '\n'
	@printf 'Build targets:\n'
	@printf '  all                      - Build all targets marked with [*]\n'
	@printf '* x15                      - Build the kernel ELF image\n'
	@printf '\n'
	@printf 'Documentation targets:\n'
	@printf '* docs                     - Build all documentation\n'
	$(DOC_HELP)
	@printf '\n'
	@printf 'Installation targets:\n'
	@printf '  install                  - Install the kernel and documentation\n'
	@printf '  install-strip            - Same as install but also strip the kernel\n'
	@printf '  install-x15              - Install the kernel only\n'
	@printf '  install-strip-x15        - Same as install-x15 but also strip the kernel\n'
	@printf '  install-docs             - Install documentation files\n'
	$(DOC_INSTALL_HELP)
	@printf '\n'
	@printf 'Architecture specific targets ($(ARCH)):\n'
	@$(if $(BOARDS), \
		$(foreach b, $(BOARDS), \
		printf "  %-24s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));))
	@$(ARCH_HELP)
	@printf '\n'
	@printf 'Options:\n'
	@printf '  make V=0                 - Quiet build\n'
	@printf '  make V=1                 - Verbose build\n'
	@printf '\n'
	@printf 'Notes:\n'
	@printf '- One target at most may be specified.\n'
	@printf '- The compiler program and flags may be given at configuration time\n'
	@printf '  through the CC and CFLAGS variables respectively.\n'
	@printf '- Out-of-tree builds are performed with the following command:\n'
	@printf '    make -f path/to/src/Makefile [options] [target]\n'
	@printf '- The source directory must be completely clean for reliable out-of-tree builds.\n'
	@printf '- Use the DESTDIR and PREFIX variables to control installation, e.g.:\n'
	@printf '    make DESTDIR=/stagingroot PREFIX=/usr install\n'
	@printf '\n'
	@printf 'See README for more information.\n'

# Don't create a %config pattern rule as it would conflict with .config
KCONFIG_TARGETS := config nconfig menuconfig xconfig gconfig \
                   allnoconfig allyesconfig alldefconfig randconfig \
                   oldconfig olddefconfig defconfig savedefconfig \
                   listnewconfig

.PHONY: $(KCONFIG_TARGETS)
$(KCONFIG_TARGETS):
	$(call xbuild_kconfig_invoke)

%_defconfig:
	$(call xbuild_kconfig_invoke)

include/generated/autoconf.h: .config $(ALL_MAKEFILES)
	$(call xbuild_gen_autoconf_h)

-include .config

ifdef CONFIG_COMPILER
# Use printf to remove quotes
CC := $(shell printf -- $(CONFIG_COMPILER))
else
CC := gcc
endif

# The CC variable is used by Kconfig to set the value of CONFIG_COMPILER.
export CC

TOOLCHAIN_NAME = $(shell printf "%s" $(CC) | rev | cut -s -d - -f 2- | rev)

ifneq ($(TOOLCHAIN_NAME),)
TOOLCHAIN_PREFIX = $(TOOLCHAIN_NAME)-
endif

CPP         := $(CC) -E
NM          := $(TOOLCHAIN_PREFIX)nm
OBJCOPY     := $(TOOLCHAIN_PREFIX)objcopy

CFLAGS ?= -O2 -g

# Export to CONFIG_CFLAGS
export CFLAGS

XBUILD_CPPFLAGS :=
XBUILD_CFLAGS :=

XBUILD_CPPFLAGS += -pipe

# Do not include headers from the hosted environment, but
# do include headers from the compiler.
XBUILD_CPPFLAGS += -nostdinc
XBUILD_CPPFLAGS += -isystem $(shell $(CC) -print-file-name=include)

XBUILD_CPPFLAGS += -std=gnu11
XBUILD_CPPFLAGS += -ffreestanding
XBUILD_CPPFLAGS += -include $(SRCDIR)/kern/config.h
XBUILD_CPPFLAGS += -include include/generated/autoconf.h
XBUILD_CPPFLAGS += -I$(SRCDIR)
XBUILD_CPPFLAGS += -I$(SRCDIR)/include
XBUILD_CPPFLAGS += -I$(SRCDIR)/arch/$(ARCH)

ifndef CONFIG_ASSERT
XBUILD_CPPFLAGS += -DNDEBUG
endif

XBUILD_CFLAGS += -fsigned-char
XBUILD_CFLAGS += -fno-common

# XXX Some assemblers consider the / symbol to denote comments. The --divide
# option suppresses that behavior.
XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Wa$(COMMA)--divide)

XBUILD_CFLAGS += -Wall
XBUILD_CFLAGS += -Wextra
XBUILD_CFLAGS += -Wshadow
XBUILD_CFLAGS += -Wmissing-prototypes
XBUILD_CFLAGS += -Wstrict-prototypes

# XXX Temporary, until a single solution is adopted to silence these warnings.
XBUILD_CFLAGS += -Wno-unneeded-internal-declaration

XBUILD_CFLAGS += $(call xbuild_check_cc_option,-fno-PIE)
XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Qunused-arguments)

XBUILD_LDFLAGS += -static -nostdlib -lgcc

# Disable the build ID feature of the linker
XBUILD_LDFLAGS += -Wl,--build-id=none

x15_SOURCES-y :=
x15_LDS_S := arch/$(ARCH)/x15.lds.S

# Include the additional Makefiles here, as they may augment the build
# variables.
include $(MAKEFILE_INCLUDES)

# Export to Kconfig.
# Must be defined by the architecture-specific Makefile.
export KCONFIG_DEFCONFIG

ifdef CONFIG_COMPILER_OPTIONS
# Use printf to remove quotes
XBUILD_CFLAGS += $(shell printf -- $(CONFIG_COMPILER_OPTIONS))
endif

COMPILE := $(CC) $(XBUILD_CPPFLAGS) $(XBUILD_CFLAGS)

# Don't change preprocessor and compiler flags from this point

x15_NO_SYMTAB := .x15.no_symtab
x15_SOURCES := $(x15_SOURCES-y)
x15_OBJDEPS := $(call xbuild_replace_source_suffix,d,$(x15_SOURCES))
x15_OBJECTS := $(call xbuild_replace_source_suffix,o,$(x15_SOURCES))
x15_SYMTAB_C := .symtab.c
x15_SYMTAB_D := $(call xbuild_replace_source_suffix,d,$(x15_SYMTAB_C))
x15_SYMTAB_O := $(call xbuild_replace_source_suffix,o,$(x15_SYMTAB_C))
x15_LDS := $(basename $(x15_LDS_S))
x15_LDS_D := $(x15_LDS).d

XBUILD_LDFLAGS += -Wl,--script=$(x15_LDS)

define gen_sorted_init_ops
	$(call xbuild_action,GEN,$@) \
		$(SRCDIR)/tools/tsort_init_ops.sh "$(COMPILE)" "$@" $^
endef

.INTERMEDIATE: .x15.sorted_init_ops
.x15.sorted_init_ops: $(filter %.c,$(x15_SOURCES)) include/generated/autoconf.h
	$(call gen_sorted_init_ops)

# Adding the sorted initialization operations as a dependency allows checking
# for loops at build time.
x15_DEPS := $(x15_LDS) .x15.sorted_init_ops

# Compiling produces dependency rules as a side-effect. When the dependency
# rules file doesn't exist, the main source file is enough to trigger a
# rebuild. Afterwards, the dependency rules file is included here and the
# rules provide correct incremental compilation.
-include $(x15_OBJDEPS) $(x15_LDS_D)

%.o: %.c include/generated/autoconf.h
	$(xbuild_compile)

%.o: %.S include/generated/autoconf.h
	$(xbuild_compile)

%.lds: %.lds.S include/generated/autoconf.h
	$(xbuild_gen_linker_script)

ifeq ($(CONFIG_SYMTAB),y)
x15_FIRST_PASS := $(x15_NO_SYMTAB)
else
x15_FIRST_PASS := x15
endif

$(x15_FIRST_PASS): $(x15_OBJECTS) $(x15_DEPS)
	$(call xbuild_link,$(x15_OBJECTS))

ifeq ($(CONFIG_SYMTAB),y)
$(x15_SYMTAB_C): $(x15_FIRST_PASS) $(XBUILD_GEN_SYMTAB_DEPS)
	$(call xbuild_gen_symtab)

x15: $(x15_NO_SYMTAB) $(x15_SYMTAB_O)
	$(call xbuild_link,$(x15_OBJECTS) $(x15_SYMTAB_O))
endif

.PHONY: install-x15
install-x15:
	install -D -m 644 x15 $(DESTDIR)/boot/x15

.PHONY: install-strip-x15
install-strip-x15:
	install -s -D -m 644 x15 $(DESTDIR)/boot/x15

.PHONY: install
install: install-x15 install-docs

.PHONY: install-strip
install-strip: install-strip-x15 install-docs

.PHONY: clean
clean: clean-docs
	$(Q)$(MAKE) -f $(SRCDIR)/$(KCONFIG_PATH)/Makefile $@
	$(call xbuild_clean)

.PHONY: distclean
distclean: clean distclean-docs
	$(Q)$(MAKE) -f $(SRCDIR)/$(KCONFIG_PATH)/Makefile $@
	$(call xbuild_distclean)

path: Makefile


code:
X15 is an open source real-time microkernel intended to provide a performant
and scalable environment for cache-coherent multiprocessor machines.

The doc/intro.9.txt file is the starting page of the kernel manual, where
more information about building the kernel, the programming interfaces, or
the coding style, can be found.

See https://www.sceen.net/x15/.

Bug reports should be sent to rbraun@sceen.net.

path: README


code:
menu "Architecture-specific options"

config 64BITS
	bool "64-bits kernel" if ARCH = "x86"
	default ARCH != "i386"
	---help---
	  Build a 64-bits kernel.

config X86_PAE
	bool "Enable PAE (Physical Address Extension)"
	depends on X86_32
	---help---
	  PAE allows addressing physical memory beyond 4 GiB at the cost
	  of more pagetable lookup and memory overhead.

config X86_PMU_AMD
	bool "Enable AMD PMU driver"
	select PERFMON
	default n
	---help---
	  Enable support for the performance monitoring unit on AMD
	  processors.

	  If unsure, disable.

config X86_PMU_INTEL
	bool "Enable Intel PMU driver"
	select PERFMON
	default n
	---help---
	  Enable support for the performance monitoring unit on Intel
	  processors.

	  If unsure, disable.

endmenu

config X86_32
	def_bool y
	depends on !64BITS

config X86_64
	def_bool y
	depends on 64BITS

config X86
	def_bool y

config LATOMIC_REPLACE_ATOMIC_ON_UP
	def_bool y

config SUBARCH
	string
	default "i386" if X86_32
	default "amd64" if X86_64


code:
define run_qemu_x86
	$(call xbuild_action,QEMU,$@) \
		$(SRCDIR)/arch/x86/tools/qemu.sh "$(OBJCOPY)"
endef

.PHONY: qemu
qemu: x15
	$(call run_qemu_x86)

ifeq ($(shell uname -m),x86_64)
  KCONFIG_DEFCONFIG := amd64_defconfig
else
  KCONFIG_DEFCONFIG := i386_defconfig
endif

define ARCH_HELP
	@printf '  qemu                     - Run kernel in QEMU\n'
endef

# Prevent GCC from generating any FP code by mistake.
XBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow
XBUILD_CFLAGS += $(call cc-option,-mno-avx,)

# Store unwind information in .debug_frame instead of .eh_frame. Unwind
# tables aren't used at runtime, so using a debug section reduces the kernel
# code size.
XBUILD_CFLAGS += -fno-asynchronous-unwind-tables

# For now, use frame pointers for convenient stack tracing.
XBUILD_CFLAGS += -fno-omit-frame-pointer

XBUILD_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096

ifeq ($(CONFIG_X86_32),y)
        biarch := $(call cc-option,-m32)
        XBUILD_CPPFLAGS += -m32
else
        biarch := -m64
        XBUILD_CPPFLAGS += -m64
        XBUILD_CFLAGS += -mcmodel=kernel
endif

x15_SOURCES-y += \
        arch/x86/machine/acpi.c \
        arch/x86/machine/atcons.c \
        arch/x86/machine/atkbd.c \
        arch/x86/machine/biosmem.c \
        arch/x86/machine/boot_asm.S \
        arch/x86/machine/boot.c \
        arch/x86/machine/cga.c \
        arch/x86/machine/cpu_asm.S \
        arch/x86/machine/cpu.c \
        arch/x86/machine/ioapic.c \
        arch/x86/machine/lapic.c \
        arch/x86/machine/pic.c \
        arch/x86/machine/pit.c \
        arch/x86/machine/pmap.c \
        arch/x86/machine/ssp.c \
        arch/x86/machine/strace.c \
        arch/x86/machine/string.c \
        arch/x86/machine/tcb_asm.S \
        arch/x86/machine/tcb.c \
        arch/x86/machine/uart.c

x15_SOURCES-$(CONFIG_X86_PMU_AMD) += arch/x86/machine/pmu_amd.c
x15_SOURCES-$(CONFIG_X86_PMU_INTEL) += arch/x86/machine/pmu_intel.c


code:
/*
 * Copyright (c) 2012-2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <kern/init.h>
#include <kern/intr.h>
#include <kern/kmem.h>
#include <kern/log.h>
#include <kern/macros.h>
#include <kern/panic.h>
#include <kern/percpu.h>
#include <kern/shutdown.h>
#include <machine/acpi.h>
#include <machine/biosmem.h>
#include <machine/cpu.h>
#include <machine/io.h>
#include <machine/ioapic.h>
#include <machine/lapic.h>
#include <machine/pic.h>
#include <machine/pit.h>
#include <machine/types.h>
#include <vm/vm_kmem.h>

/*
 * Priority of the shutdown operations.
 *
 * Higher than all other methods.
 */
#define ACPI_SHUTDOWN_PRIORITY 10

#define ACPI_GAS_ASID_SYSIO 1

struct acpi_gas {
    uint8_t asid;
    uint8_t reg_width;
    uint8_t reg_offset;
    uint8_t access_size;
    uint64_t addr;
} __packed;

#define ACPI_RSDP_ALIGN 16

#define ACPI_RSDP_SIG "RSD PTR "

struct acpi_rsdp {
    uint8_t signature[8];
    uint8_t checksum;
    uint8_t oem_id[6];
    uint8_t reserved;
    uint32_t rsdt_address;
} __packed;

#define ACPI_SIG_SIZE 5

struct acpi_sdth {
    uint8_t signature[4];
    uint32_t length;
    uint8_t revision;
    uint8_t checksum;
    uint8_t oem_id[6];
    uint8_t oem_table_id[8];
    uint32_t oem_revision;
    uint8_t creator_id[4];
    uint32_t creator_revision;
} __packed;

struct acpi_rsdt {
    struct acpi_sdth header;
    uint32_t entries[0];
} __packed;

#define ACPI_MADT_ENTRY_LAPIC     0
#define ACPI_MADT_ENTRY_IOAPIC    1
#define ACPI_MADT_ENTRY_ISO       2

struct acpi_madt_entry_hdr {
    uint8_t type;
    uint8_t length;
} __packed;

#define ACPI_MADT_LAPIC_ENABLED 0x1

struct acpi_madt_entry_lapic {
    struct acpi_madt_entry_hdr header;
    uint8_t processor_id;
    uint8_t apic_id;
    uint32_t flags;
} __packed;

struct acpi_madt_entry_ioapic {
    struct acpi_madt_entry_hdr header;
    uint8_t id;
    uint8_t _reserved;
    uint32_t addr;
    uint32_t base;
} __packed;

#define ACPI_MADT_ISO_POL_DEFAULT   0x00
#define ACPI_MADT_ISO_POL_HIGH      0x01
#define ACPI_MADT_ISO_POL_LOW       0x03
#define ACPI_MADT_ISO_POL_MASK      0x03
#define ACPI_MADT_ISO_TRIG_DEFAULT  0x00
#define ACPI_MADT_ISO_TRIG_EDGE     0x04
#define ACPI_MADT_ISO_TRIG_LEVEL    0x0c
#define ACPI_MADT_ISO_TRIG_MASK     0x0c

struct acpi_madt_entry_iso {
    struct acpi_madt_entry_hdr header;
    uint8_t bus;
    uint8_t source;
    uint32_t gsi;
    int16_t flags;
} __packed;

union acpi_madt_entry {
    uint8_t type;
    struct acpi_madt_entry_hdr header;
    struct acpi_madt_entry_lapic lapic;
    struct acpi_madt_entry_ioapic ioapic;
    struct acpi_madt_entry_iso iso;
} __packed;

#define ACPI_MADT_PC_COMPAT 0x1

struct acpi_madt {
    struct acpi_sdth header;
    uint32_t lapic_addr;
    uint32_t flags;
    union acpi_madt_entry entries[0];
} __packed;

struct acpi_madt_iter {
    const union acpi_madt_entry *entry;
    const union acpi_madt_entry *end;
};

#define acpi_madt_foreach(madt, iter) \
for (acpi_madt_iter_init(iter, madt); \
     acpi_madt_iter_valid(iter);      \
     acpi_madt_iter_next(iter))

#define ACPI_FADT_FL_RESET_REG_SUP  0x400

struct acpi_fadt {
    union {
        struct acpi_sdth header;
        char unused[112];
    };

    uint32_t flags;
    struct acpi_gas reset_reg;
    uint8_t reset_value;
} __packed;

struct acpi_table_addr {
    const char *sig;
    struct acpi_sdth *table;
};

static struct acpi_table_addr acpi_table_addrs[] __initdata = {
    { "RSDT", NULL },
    { "APIC", NULL },
    { "FACP", NULL },
};

static struct acpi_gas acpi_reset_reg;
static uint8_t acpi_reset_value;

static void __init
acpi_table_sig(const struct acpi_sdth *table, char *sig)
{
    memcpy(sig, table->signature, sizeof(table->signature));
    sig[4] = '\0';
}

static int __init
acpi_table_required(const struct acpi_sdth *table)
{
    char sig[ACPI_SIG_SIZE];
    size_t i;

    acpi_table_sig(table, sig);

    for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) {
        if (strcmp(sig, acpi_table_addrs[i].sig) == 0) {
            return 1;
        }
    }

    return 0;
}

static void __init
acpi_register_table(struct acpi_sdth *table)
{
    char sig[ACPI_SIG_SIZE];
    size_t i;

    acpi_table_sig(table, sig);

    for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) {
        if (strcmp(sig, acpi_table_addrs[i].sig) == 0) {
            if (acpi_table_addrs[i].table != NULL) {
                log_warning("acpi: table %s ignored: already registered", sig);
                return;
            }

            acpi_table_addrs[i].table = table;
            return;
        }
    }

    log_warning("acpi: table '%s' ignored: unknown table", sig);
}

static struct acpi_sdth * __init
acpi_lookup_table(const char *sig)
{
    size_t i;

    for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) {
        if (strcmp(sig, acpi_table_addrs[i].sig) == 0) {
            return acpi_table_addrs[i].table;
        }
    }

    return NULL;
}

static int __init
acpi_check_tables(void)
{
    size_t i;

    for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) {
        if (acpi_table_addrs[i].table == NULL) {
            log_err("acpi: table %s missing", acpi_table_addrs[i].sig);
            return -1;
        }
    }

    return 0;
}

static void __init
acpi_free_tables(void)
{
    struct acpi_sdth *table;
    size_t i;

    for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) {
        table = acpi_table_addrs[i].table;

        if (table != NULL) {
            kmem_free(table, table->length);
        }
    }
}

static unsigned int __init
acpi_checksum(const void *ptr, size_t size)
{
    const uint8_t *bytes;
    uint8_t checksum;
    size_t i;

    bytes = ptr;
    checksum = 0;

    for (i = 0; i < size; i++) {
        checksum += bytes[i];
    }

    return checksum;
}

static int __init
acpi_check_rsdp(const struct acpi_rsdp *rsdp)
{
    unsigned int checksum;

    if (memcmp(rsdp->signature, ACPI_RSDP_SIG, sizeof(rsdp->signature)) != 0) {
        return -1;
    }

    checksum = acpi_checksum(rsdp, sizeof(*rsdp));

    if (checksum != 0) {
        return -1;
    }

    return 0;
}

static int __init
acpi_get_rsdp(phys_addr_t start, size_t size, struct acpi_rsdp *rsdp)
{
    const struct acpi_rsdp *src;
    uintptr_t addr, end, map_addr;
    size_t map_size;
    int error;

    assert(size > 0);
    assert(P2ALIGNED(size, ACPI_RSDP_ALIGN));

    if (!P2ALIGNED(start, ACPI_RSDP_ALIGN)) {
        return -1;
    }

    addr = (uintptr_t)vm_kmem_map_pa(start, size, &map_addr, &map_size);

    if (addr == 0) {
        panic("acpi: unable to map bios memory in kernel map");
    }

    for (end = addr + size; addr < end; addr += ACPI_RSDP_ALIGN) {
        src = (const struct acpi_rsdp *)addr;
        error = acpi_check_rsdp(src);

        if (!error) {
            break;
        }
    }

    if (!(addr < end)) {
        error = -1;
        goto out;
    }

    memcpy(rsdp, src, sizeof(*rsdp));
    error = 0;

out:
    vm_kmem_unmap_pa(map_addr, map_size);
    return error;
}

static int __init
acpi_find_rsdp(struct acpi_rsdp *rsdp)
{
    const uint16_t *ptr;
    uintptr_t base, map_addr;
    size_t map_size;
    int error;

    ptr = vm_kmem_map_pa(BIOSMEM_EBDA_PTR, sizeof(*ptr), &map_addr, &map_size);

    if (ptr == NULL) {
        panic("acpi: unable to map ebda pointer in kernel map");
    }

    base = *((const volatile uint16_t *)ptr);
    vm_kmem_unmap_pa(map_addr, map_size);

    if (base != 0) {
        base <<= 4;
        error = acpi_get_rsdp(base, 1024, rsdp);

        if (!error) {
            return 0;
        }
    }

    error = acpi_get_rsdp(BIOSMEM_EXT_ROM, BIOSMEM_END - BIOSMEM_EXT_ROM,
                            rsdp);

    if (!error) {
        return 0;
    }

    log_debug("acpi: unable to find root system description pointer");
    return -1;
}

static void __init
acpi_info(void)
{
    const struct acpi_sdth *rsdt;

    rsdt = acpi_lookup_table("RSDT");
    assert(rsdt != NULL);
    log_debug("acpi: revision: %u, oem: %.*s", rsdt->revision,
              (int)sizeof(rsdt->oem_id), rsdt->oem_id);
}

static struct acpi_sdth * __init
acpi_copy_table(uint32_t addr)
{
    const struct acpi_sdth *table;
    struct acpi_sdth *copy;
    uintptr_t map_addr;
    size_t size, map_size;
    unsigned int checksum;

    table = vm_kmem_map_pa(addr, sizeof(*table), &map_addr, &map_size);

    if (table == NULL) {
        panic("acpi: unable to map acpi data in kernel map");
    }

    if (!acpi_table_required(table)) {
        copy = NULL;
        goto out;
    }

    size = ((const volatile struct acpi_sdth *)table)->length;
    vm_kmem_unmap_pa(map_addr, map_size);

    table = vm_kmem_map_pa(addr, size, &map_addr, &map_size);

    if (table == NULL) {
        panic("acpi: unable to map acpi data in kernel map");
    }

    checksum = acpi_checksum(table, size);

    if (checksum != 0) {
        char sig[ACPI_SIG_SIZE];

        acpi_table_sig(table, sig);
        log_err("acpi: table %s: invalid checksum", sig);
        copy = NULL;
        goto out;
    }

    copy = kmem_alloc(size);

    if (copy == NULL) {
        panic("acpi: unable to allocate memory for acpi data copy");
    }

    memcpy(copy, table, size);

out:
    vm_kmem_unmap_pa(map_addr, map_size);
    return copy;
}

static int __init
acpi_copy_tables(const struct acpi_rsdp *rsdp)
{
    struct acpi_rsdt *rsdt;
    struct acpi_sdth *table;
    uint32_t *addr, *end;
    int error;

    table = acpi_copy_table(rsdp->rsdt_address);

    if (table == NULL) {
        return -1;
    }

    acpi_register_table(table);

    rsdt = structof(table, struct acpi_rsdt, header);
    end = (void *)rsdt + rsdt->header.length;

    for (addr = rsdt->entries; addr < end; addr++) {
        table = acpi_copy_table(*addr);

        if (table == NULL) {
            continue;
        }

        acpi_register_table(table);
    }

    error = acpi_check_tables();

    if (error) {
        goto error;
    }

    return 0;

error:
    acpi_free_tables();
    return -1;
}

static void __init
acpi_madt_iter_init(struct acpi_madt_iter *iter,
                      const struct acpi_madt *madt)
{
    iter->entry = madt->entries;
    iter->end = (void *)madt + madt->header.length;
}

static int __init
acpi_madt_iter_valid(const struct acpi_madt_iter *iter)
{
    return iter->entry < iter->end;
}

static void __init
acpi_madt_iter_next(struct acpi_madt_iter *iter)
{
    iter->entry = (void *)iter->entry + iter->entry->header.length;
}

static void __init
acpi_load_lapic(const struct acpi_madt_entry_lapic *lapic, int *is_bsp)
{
    if (!(lapic->flags & ACPI_MADT_LAPIC_ENABLED)) {
        return;
    }

    cpu_mp_register_lapic(lapic->apic_id, *is_bsp);
    *is_bsp = 0;
}

static void __init
acpi_load_ioapic(const struct acpi_madt_entry_ioapic *ioapic)
{
    ioapic_register(ioapic->id, ioapic->addr, ioapic->base);
}

static void __init
acpi_load_iso(const struct acpi_madt_entry_iso *iso)
{
    bool active_high, edge_triggered;

    if (iso->bus != 0) {
        log_err("acpi: invalid interrupt source override bus");
        return;
    }

    switch (iso->flags & ACPI_MADT_ISO_POL_MASK) {
    case ACPI_MADT_ISO_POL_DEFAULT:
    case ACPI_MADT_ISO_POL_HIGH:
        active_high = true;
        break;
    case ACPI_MADT_ISO_POL_LOW:
        active_high = false;
        break;
    default:
        log_err("acpi: invalid polarity");
        return;
    }

    switch (iso->flags & ACPI_MADT_ISO_TRIG_MASK) {
    case ACPI_MADT_ISO_TRIG_DEFAULT:
    case ACPI_MADT_ISO_TRIG_EDGE:
        edge_triggered = true;
        break;
    case ACPI_MADT_ISO_TRIG_LEVEL:
        edge_triggered = false;
        break;
    default:
        log_err("acpi: invalid trigger mode");
        return;
    }

    ioapic_override(iso->source, iso->gsi, active_high, edge_triggered);
}

static void __init
acpi_load_madt(void)
{
    const struct acpi_sdth *table;
    const struct acpi_madt *madt;
    struct acpi_madt_iter iter;
    int is_bsp;

    table = acpi_lookup_table("APIC");
    assert(table != NULL);
    madt = structof(table, struct acpi_madt, header);

    lapic_setup(madt->lapic_addr);
    is_bsp = 1;

    acpi_madt_foreach(madt, &iter) {
        switch (iter.entry->type) {
        case ACPI_MADT_ENTRY_LAPIC:
            acpi_load_lapic(&iter.entry->lapic, &is_bsp);
            break;
        case ACPI_MADT_ENTRY_IOAPIC:
            acpi_load_ioapic(&iter.entry->ioapic);
            break;
        case ACPI_MADT_ENTRY_ISO:
            acpi_load_iso(&iter.entry->iso);
            break;
        }
    }

    if (madt->flags & ACPI_MADT_PC_COMPAT) {
        pic_setup_disabled();
    }
}

static void
acpi_shutdown_reset_sysio(uint64_t addr)
{
    if (addr > UINT16_MAX) {
        log_warning("acpi: invalid sysio address");
        return;
    }

    io_write_byte((uint16_t)addr, acpi_reset_value);
}

static void
acpi_shutdown_reset(void)
{
    if ((acpi_reset_reg.reg_width != 8) || (acpi_reset_reg.reg_offset != 0)) {
        log_warning("acpi: invalid reset register");
        return;
    }

    switch (acpi_reset_reg.asid) {
    case ACPI_GAS_ASID_SYSIO:
        acpi_shutdown_reset_sysio(acpi_reset_reg.addr);
        break;
    default:
        log_warning("acpi: unsupported reset register type");
    }
}

static struct shutdown_ops acpi_shutdown_ops = {
    .reset = acpi_shutdown_reset,
};

static void __init
acpi_load_fadt(void)
{
    const struct acpi_sdth *table;
    const struct acpi_fadt *fadt;

    table = acpi_lookup_table("FACP");

    if (table == NULL) {
        log_debug("acpi: unable to find FADT table");
        return;
    }

    fadt = structof(table, struct acpi_fadt, header);

    if (!(fadt->flags & ACPI_FADT_FL_RESET_REG_SUP)) {
        log_debug("acpi: reset register not supported");
        return;
    }

    acpi_reset_reg = fadt->reset_reg;
    acpi_reset_value = fadt->reset_value;
    shutdown_register(&acpi_shutdown_ops, ACPI_SHUTDOWN_PRIORITY);
}

static int __init
acpi_setup(void)
{
    struct acpi_rsdp rsdp;
    int error;

    error = acpi_find_rsdp(&rsdp);

    if (error) {
        goto error;
    }

    error = acpi_copy_tables(&rsdp);

    if (error) {
        goto error;
    }

    acpi_info();
    acpi_load_madt();
    acpi_load_fadt();
    acpi_free_tables();

    return 0;

error:
    /*
     * For the sake of simplicity, it has been decided to ignore legacy
     * specifications such as the multiprocessor specification, and use
     * ACPI only. If ACPI is unavailable, consider the APIC system to
     * be missing and fall back to using the legacy XT-PIC and PIT.
     */
    pic_setup();
    pit_setup();
    return 0;
}

INIT_OP_DEFINE(acpi_setup,
               INIT_OP_DEP(cpu_setup, true),
               INIT_OP_DEP(intr_bootstrap, true),
               INIT_OP_DEP(kmem_setup, true),
               INIT_OP_DEP(log_setup, true),
               INIT_OP_DEP(percpu_setup, true),
               INIT_OP_DEP(shutdown_bootstrap, true),
               INIT_OP_DEP(vm_kmem_setup, true));


code:
/*
 * Copyright (c) 2012-2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * Multiprocessor information gathering module, supporting the ACPI
 * specification v1.0.
 */

#ifndef X86_ACPI_H
#define X86_ACPI_H

#include <kern/init.h>

/*
 * This init operation provides :
 *  - Multiprocessor probing
 *  - registration of I/O APIC interrupt controllers
 *  - registration of ACPI shutdown operations
 */
INIT_OP_DECLARE(acpi_setup);

#endif /* X86_ACPI_H */


code:
/*
 * Copyright (c) 2011, 2012 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef X86_ASM_H
#define X86_ASM_H

#ifndef __ASSEMBLER__
#warning "asm.h included from a C file"
#endif /* __ASSEMBLER__ */

#include <machine/cpu.h>

#define ASM_ENTRY(x)    \
.align CPU_TEXT_ALIGN;  \
.global x;              \
.type x, STT_FUNC;      \
x:

#define ASM_DATA(x)     \
.align CPU_DATA_ALIGN;  \
.global x;              \
.type x, STT_OBJECT;    \
x:

#define ASM_END(x)  \
.size x, . - x

#endif /* X86_ASM_H */


code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <stdbool.h>
#include <string.h>

#include <kern/console.h>
#include <kern/init.h>
#include <machine/atcons.h>
#include <machine/atkbd.h>
#include <machine/cga.h>

#define ATCONS_ESC_SEQ_MAX_SIZE 8

static struct console atcons_console;

typedef void (*atcons_esc_seq_fn_t)(void);

struct atcons_esc_seq {
    const char *str;
    atcons_esc_seq_fn_t fn;
};

static bool atcons_escape;
static char atcons_esc_seq[ATCONS_ESC_SEQ_MAX_SIZE];
static unsigned int atcons_esc_seq_index;

static void
atcons_process_left(void)
{
    cga_cursor_left();
}

static void
atcons_process_right(void)
{
    cga_cursor_right();
}

static const struct atcons_esc_seq atcons_esc_seqs[] = {
    { "[1D", atcons_process_left },
    { "[1C", atcons_process_right },
};

static void
atcons_reset_esc_seq(void)
{
    atcons_escape = false;
}

static int
atcons_esc_seq_lookup(const struct atcons_esc_seq **seqp)
{
    const struct atcons_esc_seq *seq;
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(atcons_esc_seqs); i++) {
        seq = &atcons_esc_seqs[i];

        if (strncmp(seq->str, atcons_esc_seq, atcons_esc_seq_index) == 0) {
            if (strlen(seq->str) == atcons_esc_seq_index) {
                *seqp = seq;
                return 0;
            } else {
                return EAGAIN;
            }
        }
    }

    return ESRCH;
}

static void
atcons_process_esc_seq(char c)
{
    const struct atcons_esc_seq *seq;
    int error;

    if (atcons_esc_seq_index >= sizeof(atcons_esc_seq)) {
        atcons_reset_esc_seq();
        return;
    }

    atcons_esc_seq[atcons_esc_seq_index] = c;
    atcons_esc_seq_index++;

    error = atcons_esc_seq_lookup(&seq);

    if (error) {
        if (error != EAGAIN) {
            atcons_reset_esc_seq();
        }
    } else {
        seq->fn();
        atcons_reset_esc_seq();
    }
}

static void
atcons_putc(struct console *console, char c)
{
    (void)console;

    if (c == '\e') {
        atcons_escape = true;
        atcons_esc_seq_index = 0;
    } else if (atcons_escape) {
        atcons_process_esc_seq(c);
    } else {
        cga_putc(c);
    }
}

static const struct console_ops atcons_ops = {
    .putc = atcons_putc,
};

static int __init
atcons_bootstrap(void)
{
    console_init(&atcons_console, "atcons", &atcons_ops);
    console_register(&atcons_console);
    return 0;
}

INIT_OP_DEFINE(atcons_bootstrap,
               INIT_OP_DEP(cga_setup, true),
               INIT_OP_DEP(console_bootstrap, true));

static int __init
atcons_setup(void)
{
    return 0;
}

INIT_OP_DEFINE(atcons_setup,
               INIT_OP_DEP(atcons_bootstrap, true),
               INIT_OP_DEP(atkbd_setup, true));

void
atcons_intr(const char *s)
{
    console_intr(&atcons_console, s);
}

void
atcons_left(void)
{
    atcons_intr("\e[D");
}

void
atcons_bottom(void)
{
    atcons_intr("\e[B");
}

void
atcons_right(void)
{
    atcons_intr("\e[C");
}

void
atcons_up(void)
{
    atcons_intr("\e[A");
}


code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * AT console driver.
 */

#ifndef X86_ATCONS_H
#define X86_ATCONS_H

#include <kern/init.h>

/*
 * Console interrupt handler.
 *
 * This function is called by the AT keyboard interrupt handler
 * to handle machine-independent console management.
 */
void atcons_intr(const char *s);

/*
 * Direction control processing functions.
 */
void atcons_left(void);
void atcons_bottom(void);
void atcons_right(void);
void atcons_up(void);

/*
 * This init operation provides :
 *  - CGA output through the console module
 */
INIT_OP_DECLARE(atcons_bootstrap);

/*
 * This init operation provides :
 *  - AT keyboard input through the console module
 *  - module fully initialized
 */
INIT_OP_DECLARE(atcons_setup);

#endif /* X86_ATCONS_H */


code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * Note that this driver is only intended to provide enough functionality
 * for the diagnostics shell. As a result, some features, especially those
 * that may not correctly be emulated for USB keyboards, will not be
 * supported. This includes any communication with the keyboard itself.
 */

#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include <kern/console.h>
#include <kern/init.h>
#include <kern/intr.h>
#include <kern/log.h>
#include <kern/macros.h>
#include <machine/atkbd.h>
#include <machine/atcons.h>
#include <machine/io.h>

/*
 * Intel 8042 I/O ports.
 */
#define ATKBD_PORT_DATA             0x60
#define ATKBD_PORT_STATUS           0x64
#define ATKBD_PORT_CMD              0x64

/*
 * Status register bits.
 */
#define ATKBD_STATUS_OUT_FULL       0x01
#define ATKBD_STATUS_IN_FULL        0x02
#define ATKBD_STATUS_TIMEOUT_ERROR  0x40
#define ATKBD_STATUS_PARITY_ERROR   0x80
#define ATKBD_STATUS_ERROR          (ATKBD_STATUS_PARITY_ERROR \
                                     | ATKBD_STATUS_TIMEOUT_ERROR)

/*
 * Controller commands.
 */
#define ATKBD_CMD_RDCONF            0x20
#define ATKBD_CMD_WRCONF            0x60
#define ATKBD_CMD_DIS2              0xa7
#define ATKBD_CMD_EN2               0xa8
#define ATKBD_CMD_DIS1              0xad
#define ATKBD_CMD_EN1               0xae

/*
 * Controller configuration register bits.
 */
#define ATKBD_CONF_ENINT1           0x01
#define ATKBD_CONF_ENINT2           0x02
#define ATKBD_CONF_ENTRANS          0x40

/*
 * Controller interrupt.
 *
 * This driver only supports keyboard input and doesn't use interrupt 12.
 */
#define ATKBD_INTR1                 1

/*
 * Key identifiers.
 *
 * Keys are obtained from static sparse tables, where undefined entries are
 * initialized to 0. As a result, make that value an invalid key.
 */
enum atkbd_key_id {
    ATKBD_KEY_INVALID = 0,
    ATKBD_KEY_1 = 1,
    ATKBD_KEY_2,
    ATKBD_KEY_3,
    ATKBD_KEY_4,
    ATKBD_KEY_5,
    ATKBD_KEY_6,
    ATKBD_KEY_7,
    ATKBD_KEY_8,
    ATKBD_KEY_9,
    ATKBD_KEY_0,
    ATKBD_KEY_DASH,
    ATKBD_KEY_EQUAL,
    ATKBD_KEY_BACKSLASH,
    ATKBD_KEY_BACKSPACE,

    ATKBD_KEY_TAB,
    ATKBD_KEY_Q,
    ATKBD_KEY_W,
    ATKBD_KEY_E,
    ATKBD_KEY_R,
    ATKBD_KEY_T,
    ATKBD_KEY_Y,
    ATKBD_KEY_U,
    ATKBD_KEY_I,
    ATKBD_KEY_O,
    ATKBD_KEY_P,
    ATKBD_KEY_OBRACKET,
    ATKBD_KEY_CBRACKET,
    ATKBD_KEY_ENTER,

    ATKBD_KEY_CAPSLOCK,
    ATKBD_KEY_A,
    ATKBD_KEY_S,
    ATKBD_KEY_D,
    ATKBD_KEY_F,
    ATKBD_KEY_G,
    ATKBD_KEY_H,
    ATKBD_KEY_J,
    ATKBD_KEY_K,
    ATKBD_KEY_L,
    ATKBD_KEY_SEMICOLON,
    ATKBD_KEY_QUOTE,

    ATKBD_KEY_LSHIFT,
    ATKBD_KEY_Z,
    ATKBD_KEY_X,
    ATKBD_KEY_C,
    ATKBD_KEY_V,
    ATKBD_KEY_B,
    ATKBD_KEY_N,
    ATKBD_KEY_M,
    ATKBD_KEY_COMMA,
    ATKBD_KEY_DOT,
    ATKBD_KEY_SLASH,
    ATKBD_KEY_RSHIFT,

    ATKBD_KEY_LCTRL,
    ATKBD_KEY_ALT,
    ATKBD_KEY_SPACE,
    ATKBD_KEY_ALTGR,
    ATKBD_KEY_RCTRL,

    ATKBD_KEY_INSERT,
    ATKBD_KEY_DELETE,
    ATKBD_KEY_HOME,
    ATKBD_KEY_END,
    ATKBD_KEY_PGUP,
    ATKBD_KEY_PGDOWN,

    ATKBD_KEY_LEFT,
    ATKBD_KEY_BOTTOM,
    ATKBD_KEY_RIGHT,
    ATKBD_KEY_UP,

    ATKBD_KEY_KP_NUMLOCK,
    ATKBD_KEY_KP_SLASH,
    ATKBD_KEY_KP_STAR,
    ATKBD_KEY_KP_MINUS,
    ATKBD_KEY_KP_HOME,
    ATKBD_KEY_KP_UP,
    ATKBD_KEY_KP_PGUP,
    ATKBD_KEY_KP_PLUS,
    ATKBD_KEY_KP_LEFT,
    ATKBD_KEY_KP_5,
    ATKBD_KEY_KP_RIGHT,
    ATKBD_KEY_KP_END,
    ATKBD_KEY_KP_BOTTOM,
    ATKBD_KEY_KP_PGDOWN,
    ATKBD_KEY_KP_ENTER,
    ATKBD_KEY_KP_INS,
    ATKBD_KEY_KP_DEL,
};

static const char atkbd_scroll_up[] = { CONSOLE_SCROLL_UP, '\0' };
static const char atkbd_scroll_down[] = { CONSOLE_SCROLL_DOWN, '\0' };

/*
 * Key modifiers.
 */
#define ATKBD_KM_SHIFT      0x01 /* Shift / caps lock modifier applies */
#define ATKBD_KM_KP         0x02 /* Num lock modifier applies */
#define ATKBD_KM_CTL        0x04 /* Unmodified key is a control character */

struct atkbd_key {
    int modifiers;
    enum atkbd_key_id id;
};

/*
 * Regular key table.
 *
 * Keys are indexed by their "scan code set 2" code. Undefined entries are
 * initialized to 0, which is a known invalid identifier. Modifiers are used
 * to select a different key->characters table, or key-specific processing
 * in the case of control keys.
 */
static const struct atkbd_key atkbd_keys[] = {
    [0x16] = { ATKBD_KM_SHIFT, ATKBD_KEY_1 },
    [0x1e] = { ATKBD_KM_SHIFT, ATKBD_KEY_2 },
    [0x26] = { ATKBD_KM_SHIFT, ATKBD_KEY_3 },
    [0x25] = { ATKBD_KM_SHIFT, ATKBD_KEY_4 },
    [0x2e] = { ATKBD_KM_SHIFT, ATKBD_KEY_5 },
    [0x36] = { ATKBD_KM_SHIFT, ATKBD_KEY_6 },
    [0x3d] = { ATKBD_KM_SHIFT, ATKBD_KEY_7 },
    [0x3e] = { ATKBD_KM_SHIFT, ATKBD_KEY_8 },
    [0x46] = { ATKBD_KM_SHIFT, ATKBD_KEY_9 },
    [0x45] = { ATKBD_KM_SHIFT, ATKBD_KEY_0 },
    [0x4e] = { ATKBD_KM_SHIFT, ATKBD_KEY_DASH },
    [0x55] = { ATKBD_KM_SHIFT, ATKBD_KEY_EQUAL },
    [0x5d] = { ATKBD_KM_SHIFT, ATKBD_KEY_BACKSLASH },
    [0x66] = { 0, ATKBD_KEY_BACKSPACE },

    [0x0d] = { 0, ATKBD_KEY_TAB },
    [0x15] = { ATKBD_KM_SHIFT, ATKBD_KEY_Q },
    [0x1d] = { ATKBD_KM_SHIFT, ATKBD_KEY_W },
    [0x24] = { ATKBD_KM_SHIFT, ATKBD_KEY_E },
    [0x2d] = { ATKBD_KM_SHIFT, ATKBD_KEY_R },
    [0x2c] = { ATKBD_KM_SHIFT, ATKBD_KEY_T },
    [0x35] = { ATKBD_KM_SHIFT, ATKBD_KEY_Y },
    [0x3c] = { ATKBD_KM_SHIFT, ATKBD_KEY_U },
    [0x43] = { ATKBD_KM_SHIFT, ATKBD_KEY_I },
    [0x44] = { ATKBD_KM_SHIFT, ATKBD_KEY_O },
    [0x4d] = { ATKBD_KM_SHIFT, ATKBD_KEY_P },
    [0x54] = { ATKBD_KM_SHIFT, ATKBD_KEY_OBRACKET },
    [0x5b] = { ATKBD_KM_SHIFT, ATKBD_KEY_CBRACKET },
    [0x5a] = { 0, ATKBD_KEY_ENTER },

    [0x58] = { ATKBD_KM_CTL, ATKBD_KEY_CAPSLOCK },
    [0x1c] = { ATKBD_KM_SHIFT, ATKBD_KEY_A },
    [0x1b] = { ATKBD_KM_SHIFT, ATKBD_KEY_S },
    [0x23] = { ATKBD_KM_SHIFT, ATKBD_KEY_D },
    [0x2b] = { ATKBD_KM_SHIFT, ATKBD_KEY_F },
    [0x34] = { ATKBD_KM_SHIFT, ATKBD_KEY_G },
    [0x33] = { ATKBD_KM_SHIFT, ATKBD_KEY_H },
    [0x3b] = { ATKBD_KM_SHIFT, ATKBD_KEY_J },
    [0x42] = { ATKBD_KM_SHIFT, ATKBD_KEY_K },
    [0x4b] = { ATKBD_KM_SHIFT, ATKBD_KEY_L },
    [0x4c] = { ATKBD_KM_SHIFT, ATKBD_KEY_SEMICOLON },
    [0x52] = { ATKBD_KM_SHIFT, ATKBD_KEY_QUOTE },

    [0x12] = { ATKBD_KM_CTL, ATKBD_KEY_LSHIFT },
    [0x1a] = { ATKBD_KM_SHIFT, ATKBD_KEY_Z },
    [0x22] = { ATKBD_KM_SHIFT, ATKBD_KEY_X },
    [0x21] = { ATKBD_KM_SHIFT, ATKBD_KEY_C },
    [0x2a] = { ATKBD_KM_SHIFT, ATKBD_KEY_V },
    [0x32] = { ATKBD_KM_SHIFT, ATKBD_KEY_B },
    [0x31] = { ATKBD_KM_SHIFT, ATKBD_KEY_N },
    [0x3a] = { ATKBD_KM_SHIFT, ATKBD_KEY_M },
    [0x41] = { ATKBD_KM_SHIFT, ATKBD_KEY_COMMA },
    [0x49] = { ATKBD_KM_SHIFT, ATKBD_KEY_DOT },
    [0x4a] = { ATKBD_KM_SHIFT, ATKBD_KEY_SLASH },
    [0x59] = { ATKBD_KM_CTL, ATKBD_KEY_RSHIFT },

    [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_LCTRL },
    [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALT },
    [0x29] = { 0, ATKBD_KEY_SPACE },

    [0x77] = { ATKBD_KM_CTL, ATKBD_KEY_KP_NUMLOCK },
    [0x7c] = { 0, ATKBD_KEY_KP_STAR },
    [0x7b] = { 0, ATKBD_KEY_KP_MINUS },
    [0x6c] = { ATKBD_KM_KP, ATKBD_KEY_KP_HOME },
    [0x75] = { ATKBD_KM_KP, ATKBD_KEY_KP_UP },
    [0x7d] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGUP },
    [0x79] = { 0, ATKBD_KEY_KP_PLUS },
    [0x6b] = { ATKBD_KM_KP, ATKBD_KEY_KP_LEFT },
    [0x73] = { 0, ATKBD_KEY_KP_5 },
    [0x74] = { ATKBD_KM_KP, ATKBD_KEY_KP_RIGHT },
    [0x69] = { ATKBD_KM_KP, ATKBD_KEY_KP_END },
    [0x72] = { ATKBD_KM_KP, ATKBD_KEY_KP_BOTTOM },
    [0x7a] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGDOWN },
    [0x70] = { ATKBD_KM_CTL | ATKBD_KM_KP, ATKBD_KEY_KP_INS },
    [0x71] = { ATKBD_KM_KP, ATKBD_KEY_KP_DEL },
};

/*
 * Key table for e0 codes.
 *
 * This table is similar to the regular key table, except it is used if an
 * e0 code was read in the input byte sequence.
 */
static const struct atkbd_key atkbd_e0_keys[] = {
    [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALTGR },
    [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_RCTRL },

    [0x70] = { ATKBD_KM_CTL, ATKBD_KEY_INSERT },
    [0x71] = { 0, ATKBD_KEY_DELETE },
    [0x6c] = { 0, ATKBD_KEY_HOME },
    [0x69] = { 0, ATKBD_KEY_END },
    [0x7d] = { ATKBD_KM_SHIFT, ATKBD_KEY_PGUP },
    [0x7a] = { ATKBD_KM_SHIFT, ATKBD_KEY_PGDOWN },

    [0x6b] = { ATKBD_KM_CTL, ATKBD_KEY_LEFT },
    [0x72] = { ATKBD_KM_CTL, ATKBD_KEY_BOTTOM },
    [0x74] = { ATKBD_KM_CTL, ATKBD_KEY_RIGHT },
    [0x75] = { ATKBD_KM_CTL, ATKBD_KEY_UP },

    [0x4a] = { 0, ATKBD_KEY_KP_SLASH },
    [0x5a] = { 0, ATKBD_KEY_KP_ENTER },
};

/*
 * Key to characters table.
 *
 * This table is used for keys with no modifiers or if none of the modifiers
 * apply at input time.
 */
static const char *atkbd_chars[] = {
    [ATKBD_KEY_1] = "1",
    [ATKBD_KEY_2] = "2",
    [ATKBD_KEY_3] = "3",
    [ATKBD_KEY_4] = "4",
    [ATKBD_KEY_5] = "5",
    [ATKBD_KEY_6] = "6",
    [ATKBD_KEY_7] = "7",
    [ATKBD_KEY_8] = "8",
    [ATKBD_KEY_9] = "9",
    [ATKBD_KEY_0] = "0",
    [ATKBD_KEY_DASH] = "-",
    [ATKBD_KEY_EQUAL] = "=",
    [ATKBD_KEY_BACKSLASH] = "\\",
    [ATKBD_KEY_BACKSPACE] = "\b",

    [ATKBD_KEY_TAB] = "\t",
    [ATKBD_KEY_Q] = "q",
    [ATKBD_KEY_W] = "w",
    [ATKBD_KEY_E] = "e",
    [ATKBD_KEY_R] = "r",
    [ATKBD_KEY_T] = "t",
    [ATKBD_KEY_Y] = "y",
    [ATKBD_KEY_U] = "u",
    [ATKBD_KEY_I] = "i",
    [ATKBD_KEY_O] = "o",
    [ATKBD_KEY_P] = "p",
    [ATKBD_KEY_OBRACKET] = "[",
    [ATKBD_KEY_CBRACKET] = "]",
    [ATKBD_KEY_ENTER] = "\n",

    [ATKBD_KEY_A] = "a",
    [ATKBD_KEY_S] = "s",
    [ATKBD_KEY_D] = "d",
    [ATKBD_KEY_F] = "f",
    [ATKBD_KEY_G] = "g",
    [ATKBD_KEY_H] = "h",
    [ATKBD_KEY_J] = "j",
    [ATKBD_KEY_K] = "k",
    [ATKBD_KEY_L] = "l",
    [ATKBD_KEY_SEMICOLON] = ";",
    [ATKBD_KEY_QUOTE] = "'",

    [ATKBD_KEY_Z] = "z",
    [ATKBD_KEY_X] = "x",
    [ATKBD_KEY_C] = "c",
    [ATKBD_KEY_V] = "v",
    [ATKBD_KEY_B] = "b",
    [ATKBD_KEY_N] = "n",
    [ATKBD_KEY_M] = "m",
    [ATKBD_KEY_COMMA] = ",",
    [ATKBD_KEY_DOT] = ".",
    [ATKBD_KEY_SLASH] = "/",

    [ATKBD_KEY_SPACE] = " ",

    [ATKBD_KEY_DELETE] = "\e[3~",
    [ATKBD_KEY_HOME] = "\e[H",
    [ATKBD_KEY_END] = "\e[F",

    [ATKBD_KEY_KP_SLASH] = "/",
    [ATKBD_KEY_KP_STAR] = "*",
    [ATKBD_KEY_KP_MINUS] = "-",
    [ATKBD_KEY_KP_HOME] = "\e[H",
    [ATKBD_KEY_KP_PLUS] = "+",
    [ATKBD_KEY_KP_5] = "5",
    [ATKBD_KEY_KP_END] = "\e[F",
    [ATKBD_KEY_KP_ENTER] = "\n",
    [ATKBD_KEY_KP_DEL] = "\e[3~",
};

/*
 * Key to characters table when the shift modifier applies.
 */
static const char *atkbd_shift_chars[] = {
    [ATKBD_KEY_1] = "!",
    [ATKBD_KEY_2] = "@",
    [ATKBD_KEY_3] = "#",
    [ATKBD_KEY_4] = "$",
    [ATKBD_KEY_5] = "%",
    [ATKBD_KEY_6] = "^",
    [ATKBD_KEY_7] = "&",
    [ATKBD_KEY_8] = "*",
    [ATKBD_KEY_9] = "(",
    [ATKBD_KEY_0] = ")",
    [ATKBD_KEY_DASH] = "_",
    [ATKBD_KEY_EQUAL] = "+",
    [ATKBD_KEY_BACKSLASH] = "|",

    [ATKBD_KEY_Q] = "Q",
    [ATKBD_KEY_W] = "W",
    [ATKBD_KEY_E] = "E",
    [ATKBD_KEY_R] = "R",
    [ATKBD_KEY_T] = "T",
    [ATKBD_KEY_Y] = "Y",
    [ATKBD_KEY_U] = "U",
    [ATKBD_KEY_I] = "I",
    [ATKBD_KEY_O] = "O",
    [ATKBD_KEY_P] = "P",
    [ATKBD_KEY_OBRACKET] = "{",
    [ATKBD_KEY_CBRACKET] = "}",

    [ATKBD_KEY_A] = "A",
    [ATKBD_KEY_S] = "S",
    [ATKBD_KEY_D] = "D",
    [ATKBD_KEY_F] = "F",
    [ATKBD_KEY_G] = "G",
    [ATKBD_KEY_H] = "H",
    [ATKBD_KEY_J] = "J",
    [ATKBD_KEY_K] = "K",
    [ATKBD_KEY_L] = "L",
    [ATKBD_KEY_SEMICOLON] = ":",
    [ATKBD_KEY_QUOTE] = "\"",

    [ATKBD_KEY_Z] = "Z",
    [ATKBD_KEY_X] = "X",
    [ATKBD_KEY_C] = "C",
    [ATKBD_KEY_V] = "V",
    [ATKBD_KEY_B] = "B",
    [ATKBD_KEY_N] = "N",
    [ATKBD_KEY_M] = "M",
    [ATKBD_KEY_COMMA] = "<",
    [ATKBD_KEY_DOT] = ">",
    [ATKBD_KEY_SLASH] = "?",

    [ATKBD_KEY_PGUP] = atkbd_scroll_up,
    [ATKBD_KEY_PGDOWN] = atkbd_scroll_down,
};

/*
 * Key to keypad characters table when the numlock modifier applies.
 */
static const char *atkbd_kp_chars[] = {
    [ATKBD_KEY_KP_HOME] = "7",
    [ATKBD_KEY_KP_UP] = "8",
    [ATKBD_KEY_KP_PGUP] = "9",
    [ATKBD_KEY_KP_LEFT] = "4",
    [ATKBD_KEY_KP_RIGHT] = "6",
    [ATKBD_KEY_KP_END] = "1",
    [ATKBD_KEY_KP_BOTTOM] = "2",
    [ATKBD_KEY_KP_PGDOWN] = "3",
    [ATKBD_KEY_KP_INS] = "0",
    [ATKBD_KEY_KP_DEL] = ".",
};

/*
 * Global flags.
 */
#define ATKBD_KF_E0         0x01    /* Current sequence includes an 0xe0 byte */
#define ATKBD_KF_F0         0x02    /* Current sequence includes an 0xf0 byte,
                                       which usually indicates a key release */
#define ATKBD_KF_LSHIFT     0x04    /* Left shift key is being pressed */
#define ATKBD_KF_RSHIFT     0x08    /* Right shift key is being pressed */
#define ATKBD_KF_NUMLOCK    0x10    /* Numlock key has been toggled on */
#define ATKBD_KF_CAPSLOCK   0x20    /* Caps lock key has been toggled on */

#define ATKBD_KF_SHIFT      (ATKBD_KF_CAPSLOCK  \
                             | ATKBD_KF_RSHIFT  \
                             | ATKBD_KF_LSHIFT)

/*
 * These flags are only accessed during interrupt handling and don't
 * require additional synchronization.
 */
static unsigned int atkbd_flags;

static uint8_t
atkbd_read_data(void)
{
    return io_read_byte(ATKBD_PORT_DATA);
}

static void
atkbd_write_data(uint8_t data)
{
    io_write_byte(ATKBD_PORT_DATA, data);
}

static int
atkbd_read_status(bool check_out)
{
    uint8_t status;

    status = io_read_byte(ATKBD_PORT_STATUS);

    /*
     * XXX This case is rare enough that it can be used to identify the lack
     * of hardware.
     */
    if (status == 0xff) {
        log_info("atkbd: no keyboard controller");
        return ENODEV;
    } else if (status & ATKBD_STATUS_PARITY_ERROR) {
        log_err("atkbd: parity error");
        return EIO;
    } else if (status & ATKBD_STATUS_TIMEOUT_ERROR) {
        log_err("atkbd: timeout error");
        return ETIMEDOUT;
    }

    if (check_out) {
        return (status & ATKBD_STATUS_OUT_FULL) ? 0 : EAGAIN;
    } else {
        return (status & ATKBD_STATUS_IN_FULL) ? EAGAIN : 0;
    }
}

static void
atkbd_write_cmd(uint8_t cmd)
{
    io_write_byte(ATKBD_PORT_CMD, cmd);
}

static int
atkbd_out_wait(void)
{
    int error;

    for (;;) {
        error = atkbd_read_status(true);

        if (error != EAGAIN) {
            break;
        }
    }

    return error;
}

static int
atkbd_in_wait(void)
{
    int error;

    for (;;) {
        error = atkbd_read_status(false);

        if (error != EAGAIN) {
            break;
        }
    }

    return error;
}

static int
atkbd_read(uint8_t *datap, bool wait)
{
    int error;

    if (wait) {
        error = atkbd_out_wait();
    } else {
        error = atkbd_read_status(true);
    }

    if (error) {
        return error;
    }

    *datap = atkbd_read_data();
    return 0;
}

static int
atkbd_write(uint8_t data)
{
    int error;

    error = atkbd_in_wait();

    if (error) {
        return error;
    }

    atkbd_write_data(data);
    return 0;
}

static int __init
atkbd_flush(void)
{
    uint8_t byte;
    int error;

    do {
        error = atkbd_read(&byte, false);
    } while (!error);

    if (error == EAGAIN) {
        error = 0;
    }

    return error;
}

static int __init
atkbd_disable(void)
{
    uint8_t byte;
    int error;

    atkbd_write_cmd(ATKBD_CMD_DIS1);
    atkbd_write_cmd(ATKBD_CMD_DIS2);
    atkbd_write_cmd(ATKBD_CMD_RDCONF);
    error = atkbd_read(&byte, true);

    if (error) {
        return error;
    }

    byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2 | ATKBD_CONF_ENINT1);
    atkbd_write_cmd(ATKBD_CMD_WRCONF);
    return atkbd_write(byte);
}

static int __init
atkbd_enable(void)
{
    uint8_t byte;
    int error;

    atkbd_write_cmd(ATKBD_CMD_EN1);

    atkbd_write_cmd(ATKBD_CMD_RDCONF);
    error = atkbd_read(&byte, true);

    if (error) {
        return error;
    }

    byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2);
    byte |= ATKBD_CONF_ENINT1;
    atkbd_write_cmd(ATKBD_CMD_WRCONF);
    error = atkbd_write(byte);

    if (error) {
        return error;
    }

    return atkbd_flush();
}

static void
atkbd_toggle_numlock(void)
{
    atkbd_flags ^= ATKBD_KF_NUMLOCK;
}

static void
atkbd_toggle_capslock(void)
{
    atkbd_flags ^= ATKBD_KF_CAPSLOCK;
}

static void
atkbd_key_process_chars(const struct atkbd_key *key,
                        const char **chars, size_t size)
{
    const char *s;

    if (key->id >= size) {
        return;
    }

    if (atkbd_flags & ATKBD_KF_F0) {
        return;
    }

    s = chars[key->id];

    if (s != NULL) {
        atcons_intr(s);
    }
}

static void
atkbd_key_process_shift(const struct atkbd_key *key)
{
    atkbd_key_process_chars(key, atkbd_shift_chars,
                            ARRAY_SIZE(atkbd_shift_chars));
}

static void
atkbd_key_process_kp(const struct atkbd_key *key)
{
    atkbd_key_process_chars(key, atkbd_kp_chars,
                            ARRAY_SIZE(atkbd_kp_chars));
}

static void
atkbd_key_process_ctl(const struct atkbd_key *key)
{
    switch (key->id) {
    case ATKBD_KEY_LSHIFT:
        if (atkbd_flags & ATKBD_KF_F0) {
            atkbd_flags &= ~ATKBD_KF_LSHIFT;
        } else {
            atkbd_flags |= ATKBD_KF_LSHIFT;
        }

        break;
    case ATKBD_KEY_RSHIFT:
        if (atkbd_flags & ATKBD_KF_F0) {
            atkbd_flags &= ~ATKBD_KF_RSHIFT;
        } else {
            atkbd_flags |= ATKBD_KF_RSHIFT;
        }

        break;
    case ATKBD_KEY_KP_NUMLOCK:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atkbd_toggle_numlock();
        }

        break;
    case ATKBD_KEY_CAPSLOCK:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atkbd_toggle_capslock();
        }
        break;
    case ATKBD_KEY_LEFT:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atcons_left();
        }

        break;
    case ATKBD_KEY_BOTTOM:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atcons_bottom();
        }

        break;
    case ATKBD_KEY_RIGHT:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atcons_right();
        }

        break;
    case ATKBD_KEY_UP:
        if (!(atkbd_flags & ATKBD_KF_F0)) {
            atcons_up();
        }

        break;
    default:
        break;
    }
}

static void
atkbd_key_process(const struct atkbd_key *key)
{
    if (key->id == ATKBD_KEY_INVALID) {
        goto out;
    }

    if ((key->modifiers & ATKBD_KM_SHIFT) && (atkbd_flags & ATKBD_KF_SHIFT)) {
        atkbd_key_process_shift(key);
    } else if ((key->modifiers & ATKBD_KM_KP)
               && (atkbd_flags & ATKBD_KF_NUMLOCK)) {
        atkbd_key_process_kp(key);
    } else if (key->modifiers & ATKBD_KM_CTL) {
        atkbd_key_process_ctl(key);
    } else {
        atkbd_key_process_chars(key, atkbd_chars, ARRAY_SIZE(atkbd_chars));
    }

out:
    atkbd_flags &= ~ATKBD_KF_F0;
}

static void
atkbd_process_e0_code(uint8_t code)
{
    if (code == 0xf0) {
        atkbd_flags |= ATKBD_KF_F0;
        return;
    }

    if (code >= ARRAY_SIZE(atkbd_e0_keys)) {
        goto out;
    }

    atkbd_key_process(&atkbd_e0_keys[code]);

out:
    atkbd_flags &= ~ATKBD_KF_E0;
}

static void
atkbd_process_code(uint8_t code)
{
    if (code == 0xe0) {
        atkbd_flags |= ATKBD_KF_E0;
        return;
    } else if (code == 0xf0) {
        atkbd_flags |= ATKBD_KF_F0;
        return;
    }

    if (code >= ARRAY_SIZE(atkbd_keys)) {
        return;
    }

    atkbd_key_process(&atkbd_keys[code]);
}

static int
atkbd_intr(void *arg)
{
    uint8_t code;
    int error;

    (void)arg;

    for (;;) {
        error = atkbd_read(&code, false);

        if (error) {
            return 0;
        }

        if (atkbd_flags & ATKBD_KF_E0) {
            atkbd_process_e0_code(code);
        } else {
            atkbd_process_code(code);
        }
    }

    return 0;
}

static int __init
atkbd_setup(void)
{
    int error;

    error = atkbd_flush();

    if (error) {
        return 0;
    }

    error = atkbd_disable();

    if (error) {
        return 0;
    }

    error = intr_register(ATKBD_INTR1, atkbd_intr, NULL);

    if (error) {
        log_err("atkbd: unable to register interrupt handler");
        return 0;
    }

    error = atkbd_enable();

    if (error) {
        return 0;
    }

    return 0;
}

INIT_OP_DEFINE(atkbd_setup,
               INIT_OP_DEP(atcons_bootstrap, true),
               INIT_OP_DEP(intr_setup, true));


code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * Tiny AT keyboard driver.
 */

#ifndef X86_ATKBD_H
#define X86_ATKBD_H

#include <kern/init.h>

/*
 * This init operation provides :
 *  - module fully initialized
 */
INIT_OP_DECLARE(atkbd_setup);

#endif /* X86_ATKBD_H */


code:
/*
 * Copyright (c) 2012-2018 Richard Braun.
 * Copyright (c) 2017 Agustina Arzille.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * Architecture-specific definitions for atomic operations.
 */

#ifndef X86_ATOMIC_H
#define X86_ATOMIC_H

#ifndef KERN_ATOMIC_H
#error "don't include <machine/atomic.h> directly, use <kern/atomic.h> instead"
#endif

#include <stdbool.h>

#include <kern/atomic_types.h>
#include <kern/macros.h>

#ifdef __LP64__

/* Report that 64-bits operations are supported */
#define ATOMIC_HAVE_64B_OPS

#else /* __LP64__ */

/*
 * XXX Clang doesn't provide any __atomic_xxx_8 functions on i386.
 */
#ifndef __clang__

/* Report that 64-bits operations are supported */
#define ATOMIC_HAVE_64B_OPS

/*
 * On i386, GCC generates either an FP-stack read/write, or an SSE2
 * store/load to implement these 64-bit atomic operations. Since that's not
 * feasible in the kernel, fall back to cmpxchg8b.
 *
 * XXX Note that, in this case, loading becomes a potentially mutating
 * operation, but it's not expected to be a problem since atomic operations
 * are normally not used on read-only memory.
 *
 * Also note that this assumes the processor is at least an i586.
 */

static inline unsigned long long
atomic_i386_load_64(union atomic_constptr_64 ptr, int memorder)
{
    unsigned long long prev;

    prev = 0;
    __atomic_compare_exchange_n((unsigned long long *)(ptr.ull_ptr),
                                &prev, 0, false, memorder, __ATOMIC_RELAXED);
    return prev;
}
#define atomic_load_64 atomic_i386_load_64

static inline void
atomic_i386_store_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                     int memorder)
{
    unsigned long long prev;
    bool done;

    prev = *ptr.ull_ptr;

    do {
        done = __atomic_compare_exchange_n(ptr.ull_ptr, &prev, val.ull,
                                           false, memorder, __ATOMIC_RELAXED);
    } while (!done);
}
#define atomic_store_64 atomic_i386_store_64

#endif /* __clang__ */

#endif /* __LP64__ */

#endif /* X86_ATOMIC_H */